Javascript 等待所有承诺完成,即使有些承诺被拒绝

Javascript 等待所有承诺完成,即使有些承诺被拒绝,javascript,promise,es6-promise,Javascript,Promise,Es6 Promise,假设我有一组发出网络请求的Promises,其中一个将失败: // http://does-not-exist will throw a TypeError var arr = [ fetch('index.html'), fetch('http://does-not-exist') ] Promise.all(arr) .then(res => console.log('success', res)) .catch(err => console.log('error',

假设我有一组发出网络请求的
Promise
s,其中一个将失败:

// http://does-not-exist will throw a TypeError
var arr = [ fetch('index.html'), fetch('http://does-not-exist') ]

Promise.all(arr)
  .then(res => console.log('success', res))
  .catch(err => console.log('error', err)) // This is executed   
比方说,我想等到所有这些都完成了,不管有没有失败。对于我可以不使用的资源,可能存在网络错误,但如果我可以获得,我希望在继续之前获得。我想优雅地处理网络故障


由于没有留下任何空间,在不使用promises库的情况下,建议采用什么模式来处理此问题?

我不知道您正在使用哪个promise库,但大多数都有类似的模式

编辑:好,因为您想使用没有外部库的普通ES6,所以没有这样的方法


换句话说:您必须手动循环您的承诺,并在所有承诺解决后立即解决新的组合承诺。

更新,您可能希望使用内置本机:

有趣的是,下面的答案是将该方法添加到语言中的现有技术:]


当然,您只需要一个
反射

const reflect = p => p.then(v => ({v, status: "fulfilled" }),
                            e => ({e, status: "rejected" }));

reflect(promise).then((v => {
    console.log(v.status);
});
或使用ES5:

function reflect(promise){
    return promise.then(function(v){ return {v:v, status: "fulfilled" }},
                        function(e){ return {e:e, status: "rejected" }});
}


reflect(promise).then(function(v){
    console.log(v.status);
});
或者在你的例子中:

var arr = [ fetch('index.html'), fetch('http://does-not-exist') ]

Promise.all(arr.map(reflect)).then(function(results){
    var success = results.filter(x => x.status === "fulfilled");
});

我真的很喜欢本杰明的回答,以及他如何把所有的承诺变成总是解决问题,但有时会出现错误的结果
这是我对你的要求的尝试,以防你在寻找替代品。此方法仅将错误视为有效结果,其编码与
Promise类似。否则:

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);
  });
}
Promise.settle=函数(promises){
var结果=[];
var done=承诺长度;
返回新承诺(函数(解析){
函数tryResolve(i,v){
结果[i]=v;
完成=完成-1;
如果(完成==0)
决心(结果);
}

对于(var i=0;iBenjamin的回答为解决这个问题提供了一个很好的抽象,但我希望得到一个不那么抽象的解决方案。解决这个问题的明确方法是简单地调用内部承诺的
.catch
,并从回调中返回错误

let a = new Promise((res, rej) => res('Resolved!')),
    b = new Promise((res, rej) => rej('Rejected!')),
    c = a.catch(e => { console.log('"a" failed.'); return e; }),
    d = b.catch(e => { console.log('"b" failed.'); return e; });

Promise.all([c, d])
  .then(result => console.log('Then', result)) // Then ["Resolved!", "Rejected!"]
  .catch(err => console.log('Catch', err));

Promise.all([a.catch(e => e), b.catch(e => e)])
  .then(result => console.log('Then', result)) // Then ["Resolved!", "Rejected!"]
  .catch(err => console.log('Catch', err));

再进一步,您可以编写一个通用的catch处理程序,如下所示:

const catchHandler = error => ({ payload: error, resolved: false });
那你就可以了

> Promise.all([a, b].map(promise => promise.catch(catchHandler))
    .then(results => console.log(results))
    .catch(() => console.log('Promise.all failed'))
< [ 'Resolved!',  { payload: Promise, resolved: false } ]
现在你可以这样做了:

> Promise.all([a, b].map(result => result.then(successHandler).catch(catchHandler))
    .then(results => console.log(results.filter(result => result.resolved))
    .catch(() => console.log('Promise.all failed'))
< [ 'Resolved!' ]
现在的样子

> Promise.all([a, b].map(result => result.then(successHandler).catch(catchHandler))
    .then(results => console.log(results.filter(result => result.resolved))
    .catch(() => console.log('Promise.all failed'))
< [ 'Resolved!' ]
这不会被
a.catch
捕获,所以

> Promise.all([a, b].map(promise => promise.catch(e => e))
    .then(results => console.log(results))
< [ Error, Error ]
在我的例子中,我不需要知道错误或它是如何失败的——我只关心我是否有值。我会让生成承诺的函数担心记录特定的错误

const apiMethod = () => fetch()
  .catch(error => {
    console.log(error.message);
    throw error;
  });
这样,应用程序的其余部分可以根据需要忽略其错误,并根据需要将其视为未定义的值

我希望我的高级函数能够安全地失效,而不必担心它的依赖项失效的细节,而且我还希望在必须进行权衡时,不要轻举妄动——这就是我选择不使用
reflect
的最终原因

var err;
Promise.all([
    promiseOne().catch(function(error) { err = error;}),
    promiseTwo().catch(function(error) { err = error;})
]).then(function() {
    if (err) {
        throw err;
    }
});

承诺。所有
都将接受任何被拒绝的承诺,并将错误存储在一个变量中,因此当所有承诺都已解决时,它将返回。然后您可以重新抛出错误,或者执行任何操作。这样,我想您将得到最后一次拒绝,而不是第一次拒绝。

类似的答案,但对于ES6 perh来说更为惯用aps:

const a=Promise.resolve(1);
常量b=承诺.拒绝(新错误(2));
常数c=承诺。解决(3);
Promise.all([a,b,c].map(p=>p.catch(e=>e)))
.then(results=>console.log(results))//1,错误:2,3
.catch(e=>console.log(e));
const console={log:msg=>div.innerHTML+=msg+“
”};
我会:

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

这应符合:


您可以通过synchronous executor按顺序执行逻辑。它将在每次承诺时暂停,等待解决/拒绝,并将解决结果分配给
data
属性,或引发异常(用于处理您需要的try/catch块)。以下是一个示例:

函数synchronousCode(){
函数myFetch(url){
试一试{
返回window.fetch(url.data);
}
捕获(e){
返回{状态:'失败:'+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]。状态,arr[1]。状态,arr[2]。状态);
};
run(synchronousCode,{},function(){
console.log('done');
});

我遇到了同样的问题,并通过以下方式解决了它:

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(......)
在这种情况下,
承诺。所有
将等待每个承诺进入
已解决
已拒绝
状态


有了这个解决方案,我们以一种非阻塞的方式“停止
catch
执行”。事实上,我们并没有停止任何操作,我们只是在挂起状态下返回
承诺,当超时后解决时,它会返回另一个
承诺。

这是我的自定义
settled promiseall()

Promise.all相比

  • 如果所有承诺都得到了解决,那么它的表现将与标准承诺一模一样

  • 如果多个承诺中的一个被拒绝,它将返回第一个被拒绝的承诺,与标准承诺大致相同,但不同的是,它会等待所有承诺解决/拒绝

对于勇敢的人,我们可以改变承诺

小心。一般来说,我们从不更改内置程序,因为它可能会破坏其他不相关的JS库,或者与将来对JS标准的更改相冲突。

My
settledPromiseall
向后兼容
Promise.all
并扩展其功能


开发标准的人——为什么不将其纳入新的Promise标准?

自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);
                }
            });
        });
    });
};
用法签名就像
const apiMethod = () => fetch()
  .catch(error => {
    console.log(error.message);
    throw error;
  });
var err;
Promise.all([
    promiseOne().catch(function(error) { err = error;}),
    promiseTwo().catch(function(error) { err = error;})
]).then(function() {
    if (err) {
        throw err;
    }
});
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
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,
        }))));
    };
}
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(......)
const settledPromiseAll = function(promisesArray) {
  var savedError;

  const saveFirstError = function(error) {
    if (!savedError) savedError = error;
  };
  const handleErrors = function(value) {
    return Promise.resolve(value).catch(saveFirstError);
  };
  const allSettled = Promise.all(promisesArray.map(handleErrors));

  return allSettled.then(function(resolvedPromises) {
    if (savedError) throw savedError;
    return resolvedPromises;
  });
};
(function() {
  var stdAll = Promise.all;

  Promise.all = function(values, wait) {
    if(!wait)
      return stdAll.call(Promise, values);

    return settledPromiseAll(values);
  }
})();
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);
                }
            });
        });
    });
};
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
  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;
  }
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
*/
const [value1, value2] = PromiseAllCatch(promises);
if (!(value1 instanceof Error)) console.log(value1);
const [{v: value1, e: error1}, {v: value2, e: error2}] = Promise.all(reflect..
if (!error1) { console.log(value1); }
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');
const promises = [ fetch('index.html'), fetch('https://does-not-exist/') ];
const results = await Promise.allSettled(promises);
const successfulPromises = results.filter(p => p.status === 'fulfilled');
const promise1 = //...
const promise2 = //...

const data = await Promise.all([promise1, promise2])

const dataFromPromise1 = data[0]
const dataFromPromise2 = data[1]
const settle = (promise) => (promise instanceof Promise) ?
  promise.then(val => ({ value: val, status: "fulfilled" }),
               err => ({ reason: err, status: "rejected" })) :
  { value: promise, status: 'fulfilled' };

const allSettled = async (parr) => Promise.all(parr.map(settle));