Promise结合reduce计算数组总和返回异常结果的原因及解决方法
错误原因
Array.reduce会把上一轮回调的返回值直接作为下一轮回调的第一个累加参数传入,不会自动等待Promise异步完成、取出resolve的实际值。- 现有代码第一轮执行
sumPromise(1,5)时,返回值是一个待状态变更的Promise对象,不是求和得到的数值6。第二轮执行时,累加参数就变成了这个Promise对象,和当前遍历到的数字执行a + b运算时,JS会把Promise对象隐式转换为字符串"[object Promise]",和数字做字符串拼接。 - 后续遍历过程会持续把上一轮返回的Promise对象直接传入求和,最终就得到了
[object Promise]8的错误结果。
修正方案
要基于Promise实现数组求和,核心是让reduce的累加值始终是Promise实例,每一轮都等待上一个累加Promise resolve出实际数值后,再执行下一步异步求和:
- 给reduce传入初始值
Promise.resolve(0),作为累加的起点Promise - 每一轮回调中,先等待上一轮的累加Promise拿到当前总和,再调用sumPromise计算当前总和与下一个数组元素的和,返回这个新的Promise
- 由于回调内需要等待Promise结果,将reduce的回调标记为async函数即可
修正后的完整代码:
const arr = [1, 5, 7, 8]; function sumPromise(a, b) { return new Promise((res) => { setTimeout(() => { res(a + b); }, 1000); }); } let res = arr.reduce(async (sumAcc, currentNum) => { // 等待上一步异步求和完成,拿到实际数值 const currentSum = await sumAcc; // 返回当前步的求和Promise,作为下一轮的累加值 return sumPromise(currentSum, currentNum); }, Promise.resolve(0)); res.then((total) => console.log(total)); // 运行约4秒后输出21,结果符合预期
补充说明:上述写法是串行执行异步求和,每一步等上一步1秒延时完成后再执行下一个延时,总耗时约4秒。如果需要并行执行异步逻辑再汇总结果,可以搭配
Promise.all实现,串行链式调用是最贴合原有代码逻辑的修正方式。
内容的提问来源于stack exchange,提问作者Yus
相关产品推荐
相关产品推荐

