在forEach回调函数中存在异步操作的处理
我们会碰到这种情形:
const result =[];
const ids =[1,2,3,4,5];
ids.forEach(id =>{
const list =http(id);//http为⼀个异步请求,拿到对应id的信息
result.push(list.name);
})
console.log(result);// []
苹果螺繁殖结果事与愿违是⼀个空的数组,其实我们希望的结果打印对应id的信息name;原因在于http是⼀个异步请求,那么forEach的函数回调则会把异步请求放⼊事件队列⾥,然后执⾏console.log(result),最后将事件队列⾥的函数放⼊主线程执⾏,所以我们看到打印的结果是⼀个空数组,如果我们在console.log(re
sult)后⾯加上这么⼀段代码tTimeout(() => {console.log(result)}),那么其打印的结果就是我们所要得到的信息名称;所以我们想着能不能不通过这种tTimeout去拿到我们想要的结果,⽽是通过在异步请求前加⼊await来实现我们要的结果:
const result =[];
const ids =[1,2,3,4,5];
ids.forEach(async(id)=>{
const list =await http(id);//http为⼀个异步请求,拿到对应id的信息
result.push(list.name);电影孤岛惊魂
})
console.log(result);// []
结果出乎意料,依然打印的是⼀个空的数组,所以我们这时候就需要来看看forEach⽅法是怎么封装的了,这是forEach⽅法的⼀段源码:
/
/ Production steps of ECMA-262, Edition 5, 15.4.4.18
// Reference: es5.github.io/#x15.4.4.18
if(!Array.prototype.forEach){
Array.prototype.forEach=function(callback, thisArg){
var T, k;
if(this==null){
throw new TypeError(' this is null or not defined');
}
// 1. Let O be the result of calling toObject() passing the
// |this| value as the argument.
var O=Object(this);
/
/ 2. Let lenValue be the result of calling the Get() internal
班规// method of O with the argument "length".
// 3. Let len be toUint32(lenValue).
var len =O.length >>>0;
// 4. If isCallable(callback) is fal, throw a TypeError exception. // See: /#x9.11
if(typeof callback !=="function"){
throw new TypeError(callback +' is not a function');
}
// 5. If thisArg was supplied, let T be thisArg; el let
// T be undefined.
if(arguments.length >1){
T= thisArg;
}
// 6. Let k be 0
k =0;
// 7. Repeat, while k < len
while(k < len){
var kValue;
// a. Let Pk be ToString(k).
// This is implicit for LHS operands of the in operator
// b. Let kPrent be the result of calling the HasProperty
// internal method of O with argument Pk.
/
/ This step can be combined with c
// c. If kPrent is true, then
if(k in O){
// i. Let kValue be the result of calling the Get internal
// method of O with argument Pk.
kValue =O[k];
// ii. Call the Call internal method of callback with T as
// the this value and argument list containing kValue, k, and O. callback.call(T, kValue, k,O);
}
// d. Increa k by 1.
k++;
}
// 8. return undefined
};
}
我们可以看到callback.call(T, kValue, k, o)的执⾏是⼀个同步执⾏,所以我们的回调函数加了async依然不能得到我们想要的结果,因
为http前加的await只能保证回调函数在执⾏中的http先执⾏,⽽后执⾏result.push(list.name);并不能保证forEach⽅法的回调函数全部执⾏后,再打印console.log(result);所以我们来改造⼀下forEach⽅法:
const result =[];
const ids =[1,2,3,4,5];
ids.forEach(async(id)=>{
const list =await http(id);//http为⼀个异步请求,拿到对应id的信息
result.push(list.name);
})
console.log(result);// []
结果出乎意料,依然打印的是⼀个空的数组,所以我们这时候就需要来看看forEach⽅法是怎么封装的了,这是forEach⽅法的⼀段源码:
// Production steps of ECMA-262, Edition 5, 15.4.4.18
// Reference: es5.github.io/#x15.4.4.18
abs指示灯if(!Array.prototype.forEach){
Array.prototype.forEach =async function(callback, thisArg){
数码印刷var T, k;
if(this==null){
中队长
throw new TypeError(' this is null or not defined');
}
// 1. Let O be the result of calling toObject() passing the
// |this| value as the argument.
var O=Object(this);
// 2. Let lenValue be the result of calling the Get() internal
// method of O with the argument "length".
// 3. Let len be toUint32(lenValue).
var len =O.length >>>0;
// 4. If isCallable(callback) is fal, throw a TypeError exception. // See: /#x9.11
if(typeof callback !=="function"){
throw new TypeError(callback +' is not a function');
}
// 5. If thisArg was supplied, let T be thisArg; el let
// T be undefined.
if(arguments.length >1){
T= thisArg;
}
// 6. Let k be 0
k =0;
// 7. Repeat, while k < len
while(k < len){
var kValue;
// a. Let Pk be ToString(k).鼻子上的痣
// This is implicit for LHS operands of the in operator
// b. Let kPrent be the result of calling the HasProperty
// internal method of O with argument Pk.
// This step can be combined with c
// c. If kPrent is true, then
if(k in O){
// i. Let kValue be the result of calling the Get internal
// method of O with argument Pk.
kValue =O[k];
/
/ ii. Call the Call internal method of callback with T as
蝙蝠是什么类动物
// the this value and argument list containing kValue, k, and O.
await callback.call(T, kValue, k,O);
}
// d. Increa k by 1.
k++;
}
// 8. return undefined
};
}
我们将forEach定义成⼀个异步函数,在回调函数前加上await,这样我们在http前加的awati就可以实
现我们想要的结果了;现在问题⼜来了,我们能不能在不改forEach源码得前提下,使⽤其他⽅法来得到我们需要的结果呢,答案是肯定的:
1. 利⽤Promi.all()
const result =[];
const ids =[1,2,3,4,5];
const https = ids.map(id =>{
return http(id);
});
const res =await Promi.all(https);
res.forEach(list =>{
result.push(list.name);
})
console.log(result);// []
2. 利⽤length属性
const result =[];
const ids =[1,2,3,4,5];
const{ length }= ids;
async function request(){
return new Promi((resolve)=>{
ids.forEach((id, i)=>{
const list =await http(id);
result.push(list.name);
if(i === length -1){
resolve(result);
}
})
});
}
await request();
console.log(result);