Wait until all promises complete even if some rejected

前端 未结 18 1910
醉酒成梦
醉酒成梦 2020-11-21 04:55

Let\'s say I have a set of Promises that are making network requests, of which one will fail:

// http://does-not-exist will throw a TypeError
va         


        
相关标签:
18条回答
  • 2020-11-21 05:24

    I've been using following codes since ES5.

    Promise.wait = function(promiseQueue){
        if( !Array.isArray(promiseQueue) ){
            return Promise.reject('Given parameter is not an array!');
        }
    
        if( promiseQueue.length === 0 ){
            return Promise.resolve([]);
        }
    
        return new Promise((resolve, reject) =>{
            let _pQueue=[], _rQueue=[], _readyCount=false;
            promiseQueue.forEach((_promise, idx) =>{
                // Create a status info object
                _rQueue.push({rejected:false, seq:idx, result:null});
                _pQueue.push(Promise.resolve(_promise));
            });
    
            _pQueue.forEach((_promise, idx)=>{
                let item = _rQueue[idx];
                _promise.then(
                    (result)=>{
                        item.resolved = true;
                        item.result = result;
                    },
                    (error)=>{
                        item.resolved = false;
                        item.result = error;
                    }
                ).then(()=>{
                    _readyCount++;
    
                    if ( _rQueue.length === _readyCount ) {
                        let result = true;
                        _rQueue.forEach((item)=>{result=result&&item.resolved;});
                        (result?resolve:reject)(_rQueue);
                    }
                });
            });
        });
    };
    

    The usage signature is just like Promise.all. The major difference is that Promise.wait will wait for all the promises to finish their jobs.

    0 讨论(0)
  • 2020-11-21 05:24

    Promise.all with using modern async/await approach

    const promise1 = //...
    const promise2 = //...
    
    const data = await Promise.all([promise1, promise2])
    
    const dataFromPromise1 = data[0]
    const dataFromPromise2 = data[1]
    
    0 讨论(0)
  • 2020-11-21 05:25

    This should be consistent with how Q does it:

    if(!Promise.allSettled) {
        Promise.allSettled = function (promises) {
            return Promise.all(promises.map(p => Promise.resolve(p).then(v => ({
                state: 'fulfilled',
                value: v,
            }), r => ({
                state: 'rejected',
                reason: r,
            }))));
        };
    }
    
    0 讨论(0)
  • 2020-11-21 05:26

    There is a finished proposal for a function which can accomplish this natively, in vanilla Javascript: Promise.allSettled, which has made it to stage 4, is officialized in ES2020, and is implemented in all modern environments. It is very similar to the reflect function in this other answer. Here's an example, from the proposal page. Before, you would have had to do:

    function reflect(promise) {
      return promise.then(
        (v) => {
          return { status: 'fulfilled', value: v };
        },
        (error) => {
          return { status: 'rejected', reason: error };
        }
      );
    }
    
    const promises = [ fetch('index.html'), fetch('https://does-not-exist/') ];
    const results = await Promise.all(promises.map(reflect));
    const successfulPromises = results.filter(p => p.status === 'fulfilled');
    

    Using Promise.allSettled instead, the above will be equivalent to:

    const promises = [ fetch('index.html'), fetch('https://does-not-exist/') ];
    const results = await Promise.allSettled(promises);
    const successfulPromises = results.filter(p => p.status === 'fulfilled');
    

    Those using modern environments will be able to use this method without any libraries. In those, the following snippet should run without problems:

    Promise.allSettled([
      Promise.resolve('a'),
      Promise.reject('b')
    ])
      .then(console.log);

    Output:

    [
      {
        "status": "fulfilled",
        "value": "a"
      },
      {
        "status": "rejected",
        "reason": "b"
      }
    ]
    

    For older browsers, there is a spec-compliant polyfill here.

    0 讨论(0)
  • 2020-11-21 05:26
    var err;
    Promise.all([
        promiseOne().catch(function(error) { err = error;}),
        promiseTwo().catch(function(error) { err = error;})
    ]).then(function() {
        if (err) {
            throw err;
        }
    });
    

    The Promise.all will swallow any rejected promise and store the error in a variable, so it will return when all of the promises have resolved. Then you can re-throw the error out, or do whatever. In this way, I guess you would get out the last rejection instead of the first one.

    0 讨论(0)
  • 2020-11-21 05:28

    I had the same problem and have solved it in the following way:

    const fetch = (url) => {
      return node-fetch(url)
        .then(result => result.json())
        .catch((e) => {
          return new Promise((resolve) => setTimeout(() => resolve(fetch(url)), timeout));
        });
    };
    
    tasks = [fetch(url1), fetch(url2) ....];
    
    Promise.all(tasks).then(......)
    

    In that case Promise.all will wait for every Promise will come into resolved or rejected state.

    And having this solution we are "stopping catch execution" in a non-blocking way. In fact, we're not stopping anything, we just returning back the Promise in a pending state which returns another Promise when it's resolved after the timeout.

    0 讨论(0)
提交回复
热议问题