作者:凡江林 QQ:564438737
1.问题描述
在Node中,经常存在异步执行的情况。如下代码片段所示:
let k=1;
let sum=0;
let array=[2,4,6,8,10];
array.forEach(x=>{
//模拟forEach中存在异步操作的情况。
setTimeout(()=>{
sum+=x;
},2000)
})
console.log("sum="+sum);
我们的期望结果是:sum=30。但在Node中运行,你会看到如下图所示的结果:
那问题出在哪里呢,为什么会产生与预期不一致的结果?
2.问题分析及应对策略
经过分析,出现以上结果的原因是:当forEach中存在异步操作的时候,流程会跳过forEach,直接执行后面的console.log。从而,在sum还未赋值之前,就输出了sum的结果。
那如何解决这个问题呢?当然,解决问题的方式有很多种,在此,想到了把console.log代码移动到forEach内部,如下所示:
let k=1;
let sum=0;
let array=[2,4,6,8,10];
array.forEach(x=>{
//模拟forEach中存在异步操作的情况。
setTimeout(()=>{
sum+=x;
console.log("sum="+sum);
},2000)
})
直觉告诉你,每一次forEach循环,都会执行一次console.log。如下图所示:
但我们并不需要前面4次的sum输出。如何解决呢?我们需要设置一个条件,当forEach执行最后一次加法的时候,我们就执行输出,代码如下所示:
let k=1;
let sum=0;
let array=[2,4,6,8,10];
let len=array.length;
array.forEach(x=>{
//模拟forEach中存在异步操作的情况。
setTimeout(()=>{
sum+=x;
if(k==len){
console.log("sum="+sum);
}
k=k+1;
},2000)
})
执行结果如下图所示:
3.推广应用
在Node中,模块是普遍存在的一种情况。假设我们需要在模块中利用forEach执行一个集合,然后返回对集合元素操作的最终结果。可以如下所示来避免异步操作带来的不一致问题。
function addForeach(array, callback) {
let k = 1;
let sum = 0;
//let array=[2,4,6,8,10];
let len = array.length;
array.forEach(x => {
//模拟forEach中存在异步操作的情况。
setTimeout(() => {
sum += x;
if (k == len) {
console.log("sum=" + sum);
return callback(sum);
}
k = k + 1;
}, 2000)
})
}
module.exports={
addForeach
}
但如果按照同步的思维方式,把sum的回调放在forEach外部下方,经过测试,得不到所期望的结果,有兴趣的读者可以测试一下,在此不在详述。仅把遇到的问题及解决策略分享出来,供遇到相似问题的读者参考。
(限于作者的学识和经验,定有不少疏漏和不当之处,甚至是错误也在所难免,恳请读者和同行批准指正!)