等到所有的ES6承诺完成,甚至拒绝承诺

比方说,我有一系列的networking请求的承诺,其中一个会失败:

// 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 

可以说我想等到所有这些都完成了,不pipe是否失败。 可能会有一个networking错误的资源,我可以生活没有,但如果我能得到,我想在我继续之前。 我想优雅地处理networking故障。

由于Promises.all没有留下任何空间,所以build议使用这种方式,而不使用Promise库呢?

当然,你只需要一个reflect

 function reflect(promise){ return promise.then(function(v){ return {v:v, status: "resolved" }}, 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 === "resolved"); }); 

类似的答案,但更ES6的习惯可能:

 var a = () => Promise.resolve(1); var b = () => Promise.reject(new Error(2)); var c = () => Promise.resolve(3); Promise.all([a(), b(), c()].map(p => p.catch(e => e))) .then(results => console.log(results)) // 1,Error: 2,3 .catch(e => console.log(e)); var console = { log: msg => div.innerHTML += msg + "<br>"}; 
 <div id="div"></div> 

本杰明的答案提供了一个很好的抽象来解决这个问题,但我希望有一个不太抽象的解决scheme。 解决这个问题的明确方法是简单地调用内部承诺的.catch ,并从callback中返回错误。

 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 } ] 

这个问题是被捕获的值将有一个不同于非捕获值的接口,所以要清除这个值,你可能会这样做:

 const successHandler = result => ({ payload: result, resolved: true }); 

所以,现在你可以做到这一点:

 > 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!' ] 

然后为了保持干爽,你得到本杰明的答案:

 const reflect = promise => promise .then(successHandler) .catch(catchHander) 

它现在看起来像

 > 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!' ] 

第二种解决scheme的好处是它的抽象和干燥。 缺点是你有更多的代码,你必须记住要反映所有的承诺,使事情保持一致。

我会将我的解决scheme描述为明确的,而且确实不够健壮。 界面不能保证你确切地知道承诺是成功还是失败。

例如,你可能有这样的:

 const a = Promise.resolve(new Error('Not beaking, just bad')); const b = Promise.reject(new Error('This actually didnt work')); 

这不会被一个catch抓住,所以

 > Promise.all([a, b].map(promise => promise.catch(e => e)) .then(results => console.log(results)) < [ Error, Error ] 

没有办法知道哪个是致命的,哪个不是。 如果这很重要,那么你将要执行和接口,跟踪是否成功(这reflect )。

如果你只想优雅地处理错误,那么你可以把错误当成未定义的值来处理:

 > Promise.all([a.catch(() => undefined), b.catch(() => undefined)]) .then((results) => console.log('Known values: ', results.filter(x => typeof x !== 'undefined'))) < [ 'Resolved!' ] 

就我而言,我不需要知道错误或错误,我只是在乎我是否有价值。 我会让产生诺言的函数担心logging特定的错误。

 const apiMethod = () => fetch() .catch(error => { console.log(error.message); throw error; }); 

这样,如果需要的话,应用程序的其余部分可以忽略它的错误,如果需要的话,将其视为未定义的值。

我希望我的高级function安全地失败,而不必担心为什么它的依赖关系失败的细节,而且当我必须做出这种折衷时,我也更喜欢KISS干的 – 这就是为什么我select不使用reflect

我真的很喜欢本杰明的回答,他基本上把所有的承诺都变成了总是能够解决的,但是有时却是错误的结果。 🙂
这是我的尝试,以防万一你正在寻找替代品。 这种方法简单地把错误视为有效的结果,编码类似于Promise.all :否则:

 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); }); } 

我有同样的问题,并以下列方式解决:

 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(......) 

在那种情况下, Promise.all将等待我们所有的任务将进入resolvedrejected状态。

有了这个解决scheme,我们就可以非阻塞的方式“停止执行”。 事实上,我们并没有停止任何事情,我们只是在待机状态下返回Promise当超时后parsing时返回另一个Promise

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

Promise.all将会拒绝任何被拒绝的promise,并把这个错误存储在一个variables中,所有的promise都会被parsing。 那么你可以重新抛出错误,或做任何事情。 这样,我想你会拿出最后一次拒绝,而不是第一次。

我会做:

 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 

这应该与Q如何一致:

 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, })))); }; } 

您可以通过同步执行程序nsynjs按顺序执行您的逻辑。 它会暂停每一个承诺,等待解决/拒绝,并分配结果的data属性,或抛出一个exception(处理,你将需要尝试/ catch块)。 这里是一个例子:

 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="amaksr/nsynjs/master/nsynjs.js"></script> 

我不知道你正在使用哪个承诺库,但是大多数都有类似allSettled的东西。

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

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

您需要编写自己的when.settle版本(或使用承诺库)。 这个function就像Promise.all一样起作用,但是只有当所有的承诺都已经被履行或者被拒绝的时候才能履行。

我还build议将所需资源和可选资源拆分为不同的调用,因为如果任何所需资源发生故障(使用Promise.all),就会失败。 在那个Promise的'then'function里,所有的事情都会发生。

为了完整起见,下面是一个完整的代码示例:

 function settle(promises) { var allResults = []; return new Promise(function (resolve, reject) { promises.forEach(function (promise) { promise.then(function (result) { allResults.push({status: 'fulfilled', result: result }); if(allResults.length == promises.length) { resolve(allResults); } }, function (reason) { allResults.push({status: 'rejected', reason: reason }); if(allResults.length == promises.length) { resolve(allResults); } }); }); }); } var requiredResources = ['config.json','locale.json']; var optionalResources = ['ad.json', 'stuff.json']; // let's assume 'fetch' is defined as taking an array of // resources and returning an array of promises for those // Promise.all(fetch(requiredResources)).then(function (requiredResourceResults) { settle(fetch(optionalResources)).then(function (optionalResourceResults) { // do your business logic with required resources and any // optional resources that resolved in here }); }, function (reason) { // error handling for failed required resources });