关于forEach同步异步的问题
关于forEach
同步异步的问题
验证forEach
是同步还是异步
之前遇到过一些人跟我说,
forEeach
是异步的~不能放异步代码进去执行~我当时其实挺迷惑的,好像当初学习javascript
的时候不是这样的
有些人在你身上刻上了一道疤~tmd笑嘻嘻的就跑了~留下我一个人承受痛苦~
test.html
<script>
const arr = [1, 2, 3, 4]
console.log('start')
arr.forEach(item => {
console.log(item)
})
console.log('end')
</script>
请不要在说foreach
是一个异步的了!
forEach
执行异步代码
可能你遇到的情况是
forEach
中执行的都是异步函数,你想在里面逐个执行出来!但是不行!比如下面的代码
const sleep = (ms) => {
return new Promise((resolve) => {
setTimeout(resolve, ms)
})
}
const arr = [
() => console.log("start"),
() => sleep(1000),
() => console.log(1),
() => sleep(1000),
() => console.log(2),
() => sleep(1000),
() => console.log(3),
() => sleep(1000),
() => console.log("end")
]
arr.forEach(async fn => {
await fn()
})
期待的结果是先打印start
,然后每隔一秒往下执行一次,直到end
,但是你执行会发现,这些console
会一次瞬间执行完成!
那为什么会这样呢?我们可以去mdn找到forEach
源码
// Production steps of ECMA-262, Edition 5, 15.4.4.18
// Reference: http://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 false, throw a TypeError exception.
// See: http://es5.github.com/#x9.11
if (typeof callback !== "function") {
throw new TypeError(callback + ' is not a function');
}
// 5. If thisArg was supplied, let T be thisArg; else 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 kPresent be the result of calling the HasProperty
// internal method of O with argument Pk.
// This step can be combined with c
// c. If kPresent 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. Increase k by 1.
k++;
}
// 8. return undefined
};
}
关键代码在这里
foreach
内部呢是一个while
循环,然后再内部去执行回调函数!你可以看到并没有对内部异步进行什么处理~async fn => { await fn() }
相当于每次循环,都只是回调执行了外层的
fn
,执行就完事了,而没有对内部的await
做一些操作,其实就是在循环中没有去等待执行await
的结果,所以里面的异步sleep,
还是放到异步队列去等待同步执行完成后再去执行,也就是先打印再去sleep
,所以没有sleep的效果
如果直接用for
循环去处理,那么就是针对每一次循环去做了一个异步的await
,
const sleep = (ms) => {
return new Promise((resolve) => {
setTimeout(resolve, ms)
})
}
const arr = [
() => console.log("start"),
() => sleep(1000),
() => console.log(1),
() => sleep(1000),
() => console.log(2),
() => sleep(1000),
() => console.log(3),
() => sleep(1000),
() => console.log("end")
]
async function run(arr) {
for (let i = 0; i < arr.length; i++) {
await arr[i]()
}
}
run(arr)
写一个自己的forEach
其实就是把
forEach
内部实现改成for
循环,让每次循环都能捕捉执行await
,而不是外层的async
函数
Array.prototype.asyncForEach = async function (callback, args) {
const _arr = this, // 因为调用的方式 [1,2,3].asyncForEach this指向数组
isArray = Array.isArray(_arr), //判断调用者是不是数组
_args = args ? Object(args) : window //对象化
if (!isArray) {
throw new TypeError("the caller must be a array type!")
}
for (let i = 0; i < _arr.length; i++) {
await callback.call(_args,_arr[i])
}
}
const sleep = (ms) => {
return new Promise((resolve) => {
setTimeout(resolve, ms)
})
}
const arr = [
() => console.log("start"),
() => sleep(1000),
() => console.log(1),
() => sleep(1000),
() => console.log(2),
() => sleep(1000),
() => console.log(3),
() => sleep(1000),
() => console.log("end")
]
arr.asyncForEach(async(fn)=>{
await fn()
})
测试一下,成功的!
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: