Wait until all promises complete even if some rejected

前端 未结 18 1915
醉酒成梦
醉酒成梦 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:40

    I really like Benjamin's answer, and how he basically turns all promises into always-resolving-but-sometimes-with-error-as-a-result ones. :)
    Here's my attempt at your request just in case you were looking for alternatives. This method simply treats errors as valid results, and is coded similar to Promise.all otherwise:

    Promise.settle = function(promises) {
      var results = [];
      var done = promises.length;
    
      return new Promise(function(resolve) {
        function tryResolve(i, v) {
          results[i] = v;
          done = done - 1;
          if (done == 0)
            resolve(results);
        }
    
        for (var i=0; i<promises.length; i++)
          promises[i].then(tryResolve.bind(null, i), tryResolve.bind(null, i));
        if (done == 0)
          resolve(results);
      });
    }
    
    0 讨论(0)
  • 2020-11-21 05:41

    I would do:

    var err = [fetch('index.html').then((success) => { return Promise.resolve(success); }).catch((e) => { return Promise.resolve(e); }),
    fetch('http://does-not-exist').then((success) => { return Promise.resolve(success); }).catch((e) => { return Promise.resolve(e); })];
    
    Promise.all(err)
    .then(function (res) { console.log('success', res) })
    .catch(function (err) { console.log('error', err) }) //never executed
    
    0 讨论(0)
  • 2020-11-21 05:44

    I think the following offers a slightly different approach... compare fn_fast_fail() with fn_slow_fail()... though the latter doesn't fail as such... you can check if one or both of a and b is an instance of Error and throw that Error if you want it to reach the catch block (e.g. if (b instanceof Error) { throw b; }) . See the jsfiddle.

    var p1 = new Promise((resolve, reject) => { 
        setTimeout(() => resolve('p1_delayed_resolvement'), 2000); 
    }); 
    
    var p2 = new Promise((resolve, reject) => {
        reject(new Error('p2_immediate_rejection'));
    });
    
    var fn_fast_fail = async function () {
        try {
            var [a, b] = await Promise.all([p1, p2]);
            console.log(a); // "p1_delayed_resolvement"
            console.log(b); // "Error: p2_immediate_rejection"
        } catch (err) {
            console.log('ERROR:', err);
        }
    }
    
    var fn_slow_fail = async function () {
        try {
            var [a, b] = await Promise.all([
                p1.catch(error => { return error }),
                p2.catch(error => { return error })
            ]);
            console.log(a); // "p1_delayed_resolvement"
            console.log(b); // "Error: p2_immediate_rejection"
        } catch (err) {
            // we don't reach here unless you throw the error from the `try` block
            console.log('ERROR:', err);
        }
    }
    
    fn_fast_fail(); // fails immediately
    fn_slow_fail(); // waits for delayed promise to resolve
    
    0 讨论(0)
  • 2020-11-21 05:44

    I know that this question has a lot of answers, and I'm sure must (if not all) are correct. However it was very hard for me to understand the logic/flow of these answers.

    So I looked at the Original Implementation on Promise.all(), and I tried to imitate that logic - with the exception of not stopping the execution if one Promise failed.

      public promiseExecuteAll(promisesList: Promise<any>[] = []): Promise<{ data: any, isSuccess: boolean }[]>
      {
        let promise: Promise<{ data: any, isSuccess: boolean }[]>;
    
        if (promisesList.length)
        {
          const result: { data: any, isSuccess: boolean }[] = [];
          let count: number = 0;
    
          promise = new Promise<{ data: any, isSuccess: boolean }[]>((resolve, reject) =>
          {
            promisesList.forEach((currentPromise: Promise<any>, index: number) =>
            {
              currentPromise.then(
                (data) => // Success
                {
                  result[index] = { data, isSuccess: true };
                  if (promisesList.length <= ++count) { resolve(result); }
                },
                (data) => // Error
                {
                  result[index] = { data, isSuccess: false };
                  if (promisesList.length <= ++count) { resolve(result); }
                });
            });
          });
        }
        else
        {
          promise = Promise.resolve([]);
        }
    
        return promise;
      }
    

    Explanation:
    - Loop over the input promisesList and execute each Promise.
    - No matter if the Promise resolved or rejected: save the Promise's result in a result array according to the index. Save also the resolve/reject status (isSuccess).
    - Once all Promises completed, return one Promise with the result of all others.

    Example of use:

    const p1 = Promise.resolve("OK");
    const p2 = Promise.reject(new Error(":-("));
    const p3 = Promise.resolve(1000);
    
    promiseExecuteAll([p1, p2, p3]).then((data) => {
      data.forEach(value => console.log(`${ value.isSuccess ? 'Resolve' : 'Reject' } >> ${ value.data }`));
    });
    
    /* Output: 
    Resolve >> OK
    Reject >> :-(
    Resolve >> 1000
    */
    
    0 讨论(0)
  • 2020-11-21 05:45

    Instead of rejecting, resolve it with a object. You could do something like this when you are implementing promise

    const promise = arg => {
      return new Promise((resolve, reject) => {
          setTimeout(() => {
            try{
              if(arg != 2)
                return resolve({success: true, data: arg});
              else
                throw new Error(arg)
            }catch(e){
              return resolve({success: false, error: e, data: arg})
            }
          }, 1000);
      })
    }
    
    Promise.all([1,2,3,4,5].map(e => promise(e))).then(d => console.log(d))

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

    You can execute your logic sequentially via synchronous executor nsynjs. It will pause on each promise, wait for resolution/rejection, and either assign resolve's result to data property, or throw an exception (for handling that you will need try/catch block). Here is an example:

    function synchronousCode() {
        function myFetch(url) {
            try {
                return window.fetch(url).data;
            }
            catch (e) {
                return {status: 'failed:'+e};
            };
        };
        var arr=[
            myFetch("https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"),
            myFetch("https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/NONEXISTANT.js"),
            myFetch("https://ajax.NONEXISTANT123.com/ajax/libs/jquery/2.0.0/NONEXISTANT.js")
        ];
        
        console.log('array is ready:',arr[0].status,arr[1].status,arr[2].status);
    };
    
    nsynjs.run(synchronousCode,{},function(){
        console.log('done');
    });
    <script src="https://rawgit.com/amaksr/nsynjs/master/nsynjs.js"></script>

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