问题描述:
最近在写代码中发现在forEach循环中,使用await函数跟正常的循环有区别,所以进行记录。
一般我们都将forEach叫做增强for循环,确实减少了部分代码量,但是当内部使用await的时候需要注意,下面我写了两个例子。
1、如果在循环中不使用await这个promise,区别不大
2、forEach和普通循环中执行await函数情况
(1)forEach
[1,2,3].forEach(function hehe),先执行hehe(1),假如hehe里面有异步,就不会等它执行完,继续执行hehe(2)了,所以有可能出现,整个函数执行完了,但是forEach里面的函数还没执行完
async function test(){
console.log('start');
const arr = [1,2,3]
arr.forEach(async(t)=>{
await new Promise((resolve)=>{setTimeout(()=>{resolve()},1000)})
console.log(t);
})
console.log('end');
}
test();
打印出来的结果是:start end 1 2 3
(2)普通循环
async function test() {
console.log('start');
const arr = [1, 2, 3]
for (let index = 0; index < arr.length; index++) {
const element = arr[index];
await new Promise((resolve) => { setTimeout(() => { resolve() }, 1000) })
console.log(arr[index]);
}
console.log('end');
}
test();
打印结果为: start 1 2 3 end ,这才是我们需要的结果
for(let t of arr){await func(t)},这样子是一个promise完才进行下一个,很多时候我们是需要这样的
想:如果我们需要写jest测试的时候,对于两种情况,写的测试代码也应该是稍有区别的,普通循环的话我们只需要在测试的时候用await来写,但是forEach因为在真正打印出来值之前已经结束了promise,那么写的时候是需要setTimeout设置几秒的。
3、forEach和普通循环中执行同步函数
代码我就不粘贴了,只需要把上面两处的代码中await new Promise((resolve) => { setTimeout(() => { resolve() }, 1000) })
注释掉即可,测试发现,两个循环最终结果都是:start 1 2 3 end
4、理论知识的区别:
(1)for循环是可以使用break和continue去终止循环的,但是forEach不行
(2)forEach代码量少
(3)for多数时候都可以使用,当然一般需要知道循环次数,而forEach更适合集合对象的遍历和操作
(4)注意内部使用await的情况
(5)只能对元素进行顺序的访问,只能访问数组或集合中的所有元素,循环中没有当前索引
注意:
forEach(function),这个function里面抛出的error或者reject的promise是不会被外层的try catch捕获的.
async function test(){
const arr = [1,2,3]
try{
arr.forEach((t)=>{
return Promise.reject(`error${t}`);
})
} catch(err){
console.log(err);
}
}
test();
forEach本身是同步的,假如里面的函数式同步的,抛出的error是会被外面的try捕捉到的,假如是异步的,抛出的error就捕捉不到
运行之后会出现未捕获的reject promise
如果把return Promise.reject(error${t}
)换成抛出error的话就会直接崩掉
因为这个function已经算是另一个上下文了
reject的promise是不会宕机的,回调里抛出的error外面捕获不到会宕机