写在前面
当我们想“并发执行”若干个任务的时候,我们很容易就想到了Promise.all
。但是Promise.all
有一个缺陷:当有一个任务失败的时候,就会直接进入catch
的逻辑了。这个可能并不是我们想要的结果。想象一下我们同时发出了3个网络请求,其中2个正常返回了,有一个reject
了,结果却都挂了,只能被迫进入错误处理的逻辑。我们的需求是:出错的那一个请求不会影响到正常的请求。这种情况应该怎么处理呢?
使用await,增加try..catch...逻辑
要解决上面的问题,思路很简单,只需要再外面再包一层Promise
就行了,不管内部的Promise
是resolved
或者rejected
了,外层的Promise
都resolve
就可以了。这样,Promise.all
接收到的,永远都是resolved
的Promise
。两层的Promise
看起来有些别扭,为了代码写起来稍微好看一点,我们用await
和try..catch
来处理。
/** * @param {Promise} p */async function promiseWithError(p) { try { const res = await p; return { err: 0, data: res }; } catch(e) { return { err: 1 } }}
下面,通过示例代码来看一下。
const p1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('promise resolve 1'); }, 1000);}); const p2 = new Promise((resolve, reject) => { setTimeout(() => { resolve('promise resolve 2'); }, 2000);}); const p3 = new Promise((resolve, reject) => { setTimeout(() => { reject('promise reject 3'); }, 3000);}); Promise.all([p1, p2, p3]) .then(res => { console.log('resolve:', res); }) .catch(err => { console.log('reject:', err); }); // 最终输出为:promise reject 3
我们修改一下代码:
Promise.all([p1, p2, p3].map(item => promiseWithError(item))) .then(res => { console.log('resolve:', res); }) .catch(err => { console.log('reject:', err); }); // 最终的输出为:// resolve: [{ err: 0, data: "promise resolve 1"}, { err: 0, data:"promise resolve 2"}, { err: 1 }]
我们用promiseWithError
把原本的promise
包了一层。async
可以把任何函数都转换为Promise
,最终传入Promise.all
的依然是一个Promise
数组,没有问题。
这样,我们就解决了问题:Promise.all
不会因为其中某一个Promsie
被reject
而导致整个挂掉。
Promise.allSettled
要解决文章开头提到的问题,我们还可以使用Promise.allSettled
。
Promise.allSettled()
方法返回一个在所有给定的promise已被resolve
或被reject
后的promise,并带有一个对象数组,每个对象表示对应的promise结果。依然使用上面的例子:
Promise.allSettled([p1, p2, p3]) .then(res => { console.log('resolve:', res); }) .catch(err => { console.log('reject:', err); }); // 最终输出为:// resolve: [{ status: "fulfilled", value: "promise resolve 1" }, { status: "fulfilled", value: "promise resolve 2"}, { status: "rejected", reason: "promise reject 3"} ]
目前,Promise.allSettled
处于 tc39 stage-4 阶段,马上就会正式发布了。
写在后面
本文阐述了两种处理Promise.all
中错误的方法,对日常开发是很有用处的。关于JavaScript中的异步任务相关总结,可以查看我之前的文章:
【JS基础】从JavaScript中的for...of说起(上):iterator和generator
【JS基础】从JavaScript中的for...of说起(下):async和await