本文记录了作者在研究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 over
在time end
后面执行。
其实,
JavaScript单线程指的是运行环境中负责解释和执行JavaScript代码的只有一个线程,即为JS引擎线程。
但是运行环境进程中是有多个线程的。
以浏览器为例,其一个进程中包括如下线程:
当遇到异步任务时,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
这里promise1
和promise2
在time over
之前打印了??
这里涉及一个知识点:宏任务
和微任务
。
所有异步任务分为宏任务和微任务:
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