要想 JavaScript 玩得溜,还得了解波 JavaScript 执行机制/(ㄒoㄒ)/~~。

个人博客:https://shansan.top

前言

最近看了波 JavaScript 相关的文章,不得不说,JavaScript 我还真没玩明白(给我哭~。。。)。也挺久没写文了,实习(“摸”)之余小记一波。

回顾一句话:JavaScript 是一门单线程、非阻塞、异步、解释性脚本语言。

本文的标题是:setTimeout 是到了xx ms 就执行吗,了解 Event-Loop 机制。先回答波:不是。

来看下网上的一段经典 js 代码在浏览器中「Microsoft Edge 84.0.522.63(64位)」的执行结果。

console.log('script start');
setTimeout(() => {
console.log('setTimeout');
},0);
Promise.resolve().then(() => {
console.log('promise1');
}).then(() =>{
console.log("promise2");
});
console.log('script end');

可以明显看到 setTimeout 的 callback 并非在 0 ms 后立即执行。那么,这是问什么?要了解原因,需要了解后续介绍的 Event Loop 机制。

概念一览

  • 浏览器的内核-多线程的渲染进程:页面的渲染、js 的执行、事件的循环都在渲染进程中进行。渲染进程主要包含以下几个线程:

{% fancybox %}



{% endfancybox %}

  • Task:Task 有 MicroTask 和 MacroTask 之分,MicroTask 在 Promise 出现之后引入。MacroTask 和 MicroTask 分别在以下几种场景形成:

    • MacroTask:主代码块、setTimeout、setInterval、IO 事件等。
    • MicroTask:Promise、process.nextTick 等。

浏览器中的Event Loop

有了基础概念,让我们来了解一下文章开头给出的代码是怎么执行的,代码如下:

console.log('script start');
setTimeout(() => {
console.log('setTimeout');
},0);
Promise.resolve().then(() => {
console.log('promise1');
}).then(() =>{
console.log("promise2");
});
console.log('script end');
  • 1、首先,整个代码块作为第一个 MacroTask 被执行,同步的代码直接被压入执行栈被执行「同步任务在JS引擎线程上执行」,script start 和 script end 被打印;
  • 2、setTimeout 被作为 MacroTask 处理,加入宏任务队列中;
  • 3、Promise 被作为 MicroTask 处理,加入微任务队列中;
  • 4、本次 MacroTask 处理完毕,检查微任务队列,发现 promise then 的 callback,promise1,promise2 先后打印;
  • 5、接下来执行下一个 MacroTask,即 setTimeout 推送给任务队列的 callback,打印 setTimeout。

so,代码执行结果如下:

script start
script end
promise1
promise2
setTimeout

由此,可大致了解到浏览器下 Event-Loop 执行机制大致如下:

{% folding open red, Event-Loop 执行机制 %}

  • 1、一开始,整段脚本被当作 MacroTask 执行
  • 2、执行过程中,同步代码进入可执行栈中直接执行,MacroTask 进入宏任务队列,MicroTask 进入微任务队列
  • 3、当前 MacroTask 执行完就出队,检查微任务队列,如果不为空,则依次执行微任务队列中的 MicroTask,直到微任务队列为空
  • 4、执行浏览器的 UI 线程的渲染工作「两个 MicroTask 执行空隙,有次 render 工作」
  • 6、执行队首的 MacroTask,回到 2,依此循环,直至宏任务队列和微任务队列都为空

可通过下图简单理解一波:

{% endfolding %}

由此可知道,setTimeout 中的 callback 不能按时执行是因为 Event-Loop,导致 JS 引擎线程还有其它的 task (promise MicroTask)要处理,主线程还未空闲下来。

参考