AngularJS中forEach结合异步Promise后外部变量值始终为0的解决方法
这个问题我太熟悉了——本质是异步操作的时序问题!你写的代码里,forEach是同步遍历children数组,但fetchdata()返回的是Promise,它的then回调是异步执行的。也就是说,当你在循环结束后立刻console.log(totald)的时候,所有的fetchdata请求还在后台跑,根本没执行到累加的逻辑,所以totald还是初始值0。
下面给你两种靠谱的解决方法:
方法1:用Promise.all()等待所有异步请求完成
把每个fetchdata的Promise收集起来,用Promise.all()等待所有请求都resolve后,再处理最终的totald:
var totald = 0; var domaines = []; // 收集所有fetchdata的Promise实例 var requestPromises = children.map(function(child) { return fetchdata(child.id).then(function(resp) { // 每个请求完成后累加数据 totald += resp.total; domaines.push({ id: child.id, total: resp.total, details: resp.details }); }); }); // 等所有请求都完成后,再输出最终的totald Promise.all(requestPromises).then(function() { console.log(totald); // 这里就能拿到正确的累加值了 });
为什么这个方法有效?Promise.all()会等待传入的所有Promise都执行完成(resolve),才会触发它自己的then回调。这时候所有的resp.total都已经被累加到totald里,domaines也已经填充好所有数据。
方法2:用async/await让异步代码“同步化”
如果你的环境支持ES6+的async/await(AngularJS 1.x也可以配合使用),可以用for...of循环替代forEach,让代码逻辑更直观:
async function getCalculatedTotal() { let totald = 0; const domaines = []; // 用for...of循环,它会等待每个await完成后再继续下一次循环 for (const child of children) { const resp = await fetchdata(child.id); totald += resp.total; domaines.push({ id: child.id, total: resp.total, details: resp.details }); } console.log(totald); // 循环结束后直接拿到正确值 } // 调用这个异步函数 getCalculatedTotal();
注意这里不能用forEach,因为forEach会同步遍历所有元素,不会等待await的结果——也就是说,它会一口气把所有fetchdata请求发出去,但不会等每个请求完成就继续,还是会出现原来的问题。for...of则会乖乖等待每个await执行完成后,再进入下一次循环。
核心总结
你遇到的问题本质是:同步代码的执行速度远快于异步请求。解决的关键就是等待所有异步操作都完成后,再访问累加后的变量,上面两种方法都是围绕这个核心逻辑实现的。
内容的提问来源于stack exchange,提问作者Majdi Taleb

