关于JS执行顺序
背景
众所周知,JS是单线程语言,但它支持异步操作,其核心机制就是JS引擎的事件循环。
先上一段代码:
| 1 | console.log(1) | 
背后的原因就是事件循环中的宏任务与微任务。
原理
总的来说,流程图如下:
- 
Promise中的代码块是立即执行的。下列代码可以证明: 1 
 2
 3
 4
 5
 6
 7
 8
 9console.log(1) 
 new Promise(() => {
 console.log(2)
 })
 console.log(3)
 // 1 2 3这部分很简单了,从上到下同步执行。 
- 
Promise后的then传入的方法是微任务。下面代码为V8引擎源码,注意它是用V8内部语言 Torque编写,我们只需要看它是继承了Microtask即可知它是一个微任务,无需在意更多细节:1 
 2
 3
 4
 5
 6
 7
 extern class PromiseResolveThenableJobTask extends Microtask {
 context: Context;
 promise_to_resolve: JSPromise;
 thenable: JSReceiver;
 then: JSReceiver;
 }所以说它会在最外层代码执行完后再去执行。 
- 
setTimeout或者setInterval都是宏任务。我这里实在是没找到源码哪里表明了这个东西,于是直接在 NodeJS里换个方式证明一下。在 NodeJS中,process.nextTick可以设置一个微任务,使用下列代码测试:1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15setTimeout(() => { 
 console.log(1)
 })
 process.nextTick(() => {
 console.log(2)
 })
 process.nextTick(() => {
 console.log(3)
 })
 console.log(4)
 // 4 2 3 1我们把 setTimeout放在最开始,而且不管设置了几次nextTick,setTimeout里的函数体总是最后执行的,由此可见它的确是一个宏任务。
更复杂一点
不管我的Promise怎么组合,怎么套,由于setTimeout设置的是宏任务,所以它始终在这些微任务都执行完成之后才会运行:
| 1 | setTimeout(() => { | 
可以看出,2 3 4全都是微任务,而5是最外层同步执行的代码,1是由setTimeout设置的下一个宏任务。
总结
再回到开头的程序:
| 1 | console.log(1) | 
因为1,3,5都是同步执行的,所以它们按顺序排列; 2是宏任务,会放到下一次事件循环时执行; 4是微任务,在首次运行时就把它添加到了微任务队列中,所以在下一次事件循环之前就会被执行。
通过这样的事件循环,使得单线程的JS也可以拥有异步的能力,使得如AJAX请求这样费时间的操作可以被安排到后面来执行,不影响页面的加载和渲染。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 喵喵小窝!

