在接触到闭包这个概念的时候,应该都见过下面这个例子,用闭包来解决i值错误的问题。
for (var i = 0; i <= 3; i++) {
(function (i) {
setTimeout(function() {
console.log(i)
})
})(i)
}
但是在循环中调用异步函数,修改数组元素属性时,出现了问题
先给出数组和两个函数
let arr1 = [{num: 1}, {num: 2}, {num: 3}]
function asyncFun(index) {
setTimeout(() => {
return index;
}, 500);
}
function asyncFunWithPromise(index) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(index)
return index;
}, 500);
})
}
先上ES5加闭包,运行中直接报错。
for (var i = 0; i <= arr1.length; i++) {
(function (i) {
console.log(i)
arr1[i].result = asyncFun(i) // Cannot set property 'result' of undefined
})(i)
}
// 等asyncFun函数返回时,循环中arr1[i]这个引用已随着循环结束消失
用ES6也是徒劳的,不报错,但是结果如下,这里也体现了let的一些特性。。。有空再深入琢磨
for (let index = 0; index < arr1.length; index++) {
console.log(index)
const currentItem = arr1[index];
currentItem.result = asyncFun(currentItem.num)
}
/* [
{ num: 1, result: undefined },
{ num: 2, result: undefined },
{ num: 3, result: undefined }
] */
用 async + await能解决这个问题,但整个循环相当于串行执行了,特别慢。
(async () => {
for (let index = 0; index < arr1.length; index++) {
console.log(index)
const currentItem = arr1[index];
currentItem.result = await asyncFunWithPromise(currentItem.num)
}
})()
// 返回值
// [ { num: 1, result: 0 }, { num: 2, result: 1 }, { num: 3, result: 2 } ]
forEach + async await能做到并行执行异步函数,但是无法获知异步函数何时全部返回
arr1.forEach(async (listItem, index) => {
console.log(index)
listItem.result = await asyncFunWithPromise(index)
});
console.log('done')
console.log(arr1) // [ { num: 1 }, { num: 2 }, { num: 3 } ],此处异步函数未结束
最后加入Promise.all,判断异步函数全部返回,高效。
let heighEfficiency = () => {
return new Promise((resolve, reject) => {
const promiseArr = []
arr1.forEach(async (listItem, index) => {
promiseArr.push(asyncFunWithPromise(index))
listItem.result = await promiseArr[index]
});
Promise.all(promiseArr).then(() => {
resolve()
}).catch(() => {
reject()
})
})
}
heighEfficiency().then(() => {
console.log(arr1)
})
// [ { num: 1, result: 0 }, { num: 2, result: 1 }, { num: 3, result: 2 } ]
屈服了,把数组元素传出去了。。。毕竟map,没办法
const functionWithPromise = item => {
return new Promise((resolve, reject) => {
setTimeout(() => {
item.result = item.num + ':async done'
resolve(item)
return;
}, 500);
})
}
const getData = () => {
return Promise.all(arr1.map(item => functionWithPromise(item)))
}
getData().then(data => {
console.log(data)
})
这个实现挺别扭的,应该有更佳的实现。求大佬帮忙。