JS 函数的执行时机

79 阅读2分钟

思考这段代码会打印什么?

let i = 0;
for(i = 0; i<6;i++){
    setTimeout(()=>{
        console.log(i);//打印6个6
    },0)
}

我想着应该打印输出的应该是0,1,2,3,4,5啊!为什么会打印出6个6呢?
查了一下MDN文档发现下面这个例子。

MDN的栗子
// 超时延迟部分
function fn(){
    console.log("我应该出现在第一行");
}
setTimeout(fn,0);
console.log('我应该出现在第二行');
//理想状态应该就是按照上面说的打印输出;
//可是控制台打印输出的是:
    "我应该出现在第二行"
    "我应该出现在第一行"

出现这个结果的原因是,尽管setTimeout 以0ms的延迟来调用函数,但这个任务已经被放入了队列中并且等待下一次执行;并不是立即执行;队列中的等待函数被调用之前,当前代码必须全部运行完毕,因此这里运行结果并非预想的那样。

4124293408-5ccb19fc9232d.png 遵循先来先出原则

用图可以看出setTimeoutfn会放在宏任务队列中,全部代码执行完后才会执行fn,主线程的for循环已经执行完了,此时i的值是6。fn在调用console.log(i)会打印出6个6了。
计时器由浏览器新开一个线程执行(区别于主线程),到点把回调放入宏任务队列;
主线程等待同步代码执行完毕 -> 清空当前循环微任务队列(这里微任务队列为空) -> 取出宏任务队列中下一个任务(即 setTimout 回调)执行,如此循环往复直至清空所有任务队列;

理想状态
for(let i = 0; i<6;i++){
    setTimeout(()=>{
        console.log(i);//打印0,1,2,3,4,5
    },0)
}

let i 的作用域只在循环里面。

let i=0;
for(i=0;i<6;i++){
  fn(i);
}
function fn(f){
    setTimeout(()=>{
      console.log(f)
    },0)
}

# ES6事件循环机制:浏览器中的宏任务和微任务
浏览器的微任务MicroTask和宏任务MacroTask