JavaScript的事件循环与宏微任务

本文记录了作者在研究JS的事件循环(event loop)宏任务和微任务的过程中如何抽丝剥茧,理清原理的。
阅读本文大概需要二十分钟。

一. 单线程与多线程

JavaScript的设计就是为了处理浏览器网页的交互,如果有多个线程同时操作DOM,那网页的渲染需要涉及线程安全的问题,不好控制。
JavaScript是单线程的,那么处理任务是一件接着一件处理,从上往下顺序执行:

console.log('script start')
console.log('do something...')
console.log('script end')

// script start
// do something...
// script end

那如果一个任务耗时很久的话,如:网络请求、定时器、io处理等,后面的任务也会阻塞。
那么,JavaScript是如何处理的呢?

console.log('script start')

console.log('do something...')

setTimeout(() => {
  console.log('timer over')
}, 1000)

// 点击页面
console.log('click page')

console.log('script end')

// script start
// do something...
// click page
// script end
// timer over

由上面的例子,可以看出,显然JS代码不是从上往下顺序执行的。time overtime end后面执行。
其实,

JavaScript单线程指的是运行环境中负责解释和执行JavaScript代码的只有一个线程,即为JS引擎线程

但是运行环境进程中是有多个线程的
以浏览器为例,其一个进程中包括如下线程:

  • JS引擎线程
  • 事件触发线程
  • 定时器触发线程
  • 异步http请求线程
  • GUI渲染线程

当遇到异步任务时,JS引擎会把它交给运行环境去处理,而JS引擎线程继续解释执行后面的任务,这样就实现了异步非阻塞
异步任务执行完成后,会把它的回调加到消息队列中,JS引擎在合适的时机从消息队列中取出消息并执行。

二. 同步和异步

同步代码如下:

console.log('hello 0')

console.log('hello 1')

console.log('hello 2')

// hello 0
// hello 1
// hello 2

它们会依次执行,执行完了就返回结果。
而异步代码呢?

setTimeout(() => {
  console.log('hello 0')
}, 1000)

console.log('hello 1')

// hello 1
// hello 0

上面的setTimeout会发起一个异步任务,不会阻塞代码。

三. 事件循环与消息队列

前面说到JS运行环境是多线程的,有一个线程(事件触发线程)专门负责事件循环机制和消息队列维护。

JS引擎在遇到异步任务时,会交给相应的线程单独去维护异步任务,等待某个时机(定时结束、网络请求完成…),然后由事件触发线程将异步任务的回调加入到消息队列中,消息队列中的消息等待被执行。

同时,JS引擎会维护一个执行栈,同步代码会依次加入到执行栈,结束会退出执行栈。

如果执行栈里的任务执行完成(执行栈为空),事件触发线程才会从消息队列中取出任务,放到执行栈去执行。事件触发线程会循环从消息队列中取任务,然后放到执行栈去执行,这种机制叫做事件循环机制

四. 宏任务和微任务

以上机制在ES5的情况下已经够用了,但ES6会有一些问题。

console.log('script start')

setTimeout(function() {
    console.log('timer over')
}, 0)

Promise.resolve().then(function() {
    console.log('promise1')
}).then(function() {
    console.log('promise2')
})

console.log('script end')

// script start
// script end
// promise1
// promise2
// timer over

这里promise1promise2time over之前打印了??
这里涉及一个知识点:宏任务微任务
所有异步任务分为宏任务和微任务:

  • 宏任务:setTimeout、setInterval等
  • 微任务:Promise、process.nextTick等

五. 参考

https://juejin.im/post/59e21e8551882578db27c364
https://segmentfault.com/a/1190000015559210
https://juejin.im/post/5be5a0b96fb9a049d518febc
https://juejin.im/post/5a6547d0f265da3e283a1df7#heading-6
https://www.jianshu.com/p/e073808c26e4
https://segmentfault.com/a/1190000016022069
https://segmentfault.com/a/1190000011198232
http://web.jobbole.com/84351/
https://segmentfault.com/a/1190000014242281
https://segmentfault.com/a/1190000005173218
https://github.com/jawil/Node.js/issues/2
https://juejin.im/post/5be5a0b96fb9a049d518febc

猜你喜欢

转载自blog.csdn.net/colinandroid/article/details/87895513