Event Loop

1. 参考资料

2. 整理

其中第二篇资料这一次,彻底弄懂 JavaScript 执行机制,更具有说服力,能解释一部分的异步情况。

2.1.1 知识点

js代码在执行时会将不同变量存放于内存中的不同位置,堆(heap)和栈(stack),其中堆存放着一些对象,栈存放一些基础类型变量以及对象的指针。

执行栈 => 存放同步代码。 事件队列 => 存放异步代码。

当异步事件返回结果(比如定时器时间到期,ajax请求返回完数据)时,js会将异步事件的回调添加到事件队列中。而后在执行栈已经执行完毕情况之后,会查看事件队列是否有任务,如果有任务,则将任务放到执行栈中进行调用。

异步任务分为微任务(micro task)与宏任务(macro task),对应会有微任务事件队列与宏任务事件队列: 宏任务:

  setInterval()
  setTimeout()

微任务:

当前执行栈执行完毕时会立刻处理所有微任务队列中的事件,然后再去宏任务队列取出一个事件,同一次事件循环中,微任务永远在宏任务之前执行

2.1.2 说明

文章后续还提到了 nodelibuv 的事件循环模型,以及事件循环各个阶段的解释。

2.2.1 知识点

  1. 同步和异步任务进入不同的执行"场所",同步任务进入主线程执行栈,异步任务进入 Event Table 并注册回调函数

  2. 当异步任务指定的事件完成(定时器到时,请求 success )时,Event Table 会把回调函数转移到 事件队列(Event Queue)

  3. 当主线程执行栈的任务执行完毕,会去事件队列读取函数,进入主线程执行

  4. 上述过程不断重复,形成事件循环

setTimeout: 即执行到该代码时,会进入Event Table注册回调函数,当定时器到时之后,Event Table会将回调函数转移到事件队列

setInterval: 即执行到该代码时,会进入Event Table注册回调函数,之后每隔指定的时间,Event Table会将回调函数转移到事件队列

除了广义上的同步任务和异步任务,还会将任务划分为宏任务和微任务:

  1. macro-task(宏任务):包括整体代码script,setTimeout,setInterval

  2. micro-task(微任务):Promise,process.nextTick

这个宏任务的划分比2.1资料中的划分多了一个整体代码script

2.2.2 说明

这篇文章举例了一些异步代码的例子,便于理解。同时全文表达出 [执行一个宏任务,执行当前所有微任务]为一个 event-loop

2.3.1 知识点

process.nextTick 注册的函数优先级高于 Promise, 即微任务从 Event Table 转移到事件队列并不完全根据代码中出现的顺序,还会有优先级的区别。 在评论区中发现以下说法:

process.nextTick 是放到当前同步执行栈的尾部,是一定比异步的任务队列早的。并不是因为优先级高于其他异步的任务。同步的执行栈到异步任务队列,process.nextTick 就是处于中间的那个。 当然异步下,任务队列里就分宏任务 / 微任务。但是我也觉得这里不是由优先级决定的,而是宏任务内的执行栈清空后,再清空当前宏任务内的微任务。接着以此循环再去清空下一个宏任务的执行栈,再清空微任务。

这个说法就相当于说,process.nextTick是在同步执行栈尾部,在执行异步任务队列之前的。就是说它是一个在所有其它异步任务之前的异步任务。(有空再翻资料)

即process.nextTick这段代码不管是在位置1还是位置2,输出结果都是1,7,6,8,2,4,3,5

同时以下这段代码的返回结果是不确定的:

有可能返回1,7,6,8,2,4,3,5,9,11,10,12,但也可能返回1、7、6、8、2、4、9、11、3、10、5、12,即两个setTimeout的执行可能混合。

原因在于:

这两段 setTimeout 都没有指定 timeout时间,默认是0,延时相同,被合并进同一个expired timers queue而一起执行。

当做合并为同一个宏任务?,那为啥还会有两种可能的结果?一直返回1、7、6、8、2、4、9、11、3、10、5、12 才对吧,这一块还没有理解

2.3.2 说明

这篇文章是在阅读 2.2这一次,彻底弄懂 JavaScript 执行机制之后写的,因此很适合和 2.2 连着一起看,同时还翻阅了 blinknode的源码解释了定时器0ms和1ms为什么效果是一致的,很具说服力。

2.4 代码解析

示例一

以上这段代码最终打印clock1, clock3, clock2.

针对这种嵌套调用的情况,可以根据这一次,彻底弄懂 JavaScript 执行机制去理解:

示例二

以上这段代码始终打印clock1, clock2, clock3

Last updated

Was this helpful?