使用async函数实现Promise.all时Promise reject触发报错问题
问题原因
核心是Node.js的Promise拒绝检测机制和代码执行时序的问题:
- Node会在每个事件循环周期结束时,扫描所有已经处于rejected状态、但还没绑定任何reject处理回调(
.catch()或.then()的第二个参数)的Promise,触发UnhandledPromiseRejectionWarning;如果后续才给这类Promise补上处理逻辑,还会额外触发PromiseRejectionHandledWarning。 - 你的代码中,
promises数组初始化完成时,里面的Promise.reject()就已经进入rejected状态了。但你用for循环按顺序迭代、执行到对应项的await p时,才会给这个Promise绑定错误处理(async函数的await会捕获Promise的reject,抛到外层由函数的catch处理)。如果排在rejected Promise前面的任务是异步resolve的(比如你例子里1秒延迟的takeTime(2)),在等待前面任务完成的这段时间里,rejected的Promise一直处于已失败但无处理的状态,Node扫描到就会抛出警告。 - 你之前尝试的try-catch包裹for循环、给遍历到的p加
.catch()都没用:这两种方式都是执行到对应迭代项时才绑定错误处理,reject发生和处理绑定之间的空窗期依然存在,警告还是会触发。 - 额外说明:你原本的for-await写法也不符合原生
Promise.all的语义——原生Promise.all只要有任意一个Promise reject就会立刻返回失败,而你的写法必须等await到rejected的那一项才会返回失败,不符合规范行为。
修复方案
最稳妥的方式是按照Promise.all的标准逻辑实现,遍历传入项时立刻给每个Promise绑定状态处理回调,从根源上消除无处理reject的空窗期:
function Promise_All_II(promises) { return new Promise((resolve, reject) => { const results = [] let completedCount = 0 const length = promises.length // 传入空数组直接返回空结果 if (length === 0) return resolve(results) promises.forEach((item, index) => { // 用Promise.resolve包装,兼容传入非Promise值的场景 Promise.resolve(item).then( (value) => { results[index] = value completedCount++ // 所有Promise成功后按传入顺序返回结果 if (completedCount === length) resolve(results) }, // 任意Promise失败立刻reject,处理回调是同步绑定的,不会触发未处理警告 (reason) => reject(reason) ) }) }) } // 测试代码 const takeTime = (value, time = 1000) => new Promise(resolve => setTimeout(resolve, time, value)) const promises = [ Promise.resolve(1), takeTime(2), Promise.reject('You are fairly dump'), takeTime(3) ] console.log('Promise all II:') Promise_All_II(promises).then(console.log, console.log)
如果一定要保留async/await的写法,需要在遍历前同步给所有Promise绑定catch占位,消除空窗期:
async function Promise_All_II(promises){ // 同步遍历所有项,提前绑定错误处理 const wrappedPromises = promises.map(item => Promise.resolve(item).catch(err => { throw err }) ) const results = [] for (const p of wrappedPromises) { results.push(await p) } return results }
内容的提问来源于stack exchange,提问作者Cyber World
相关产品推荐
相关产品推荐

