javascript event loop
Posted 牛顿的小脑
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了javascript event loop相关的知识,希望对你有一定的参考价值。
原文: https://blog.csdn.net/sjn0503/article/details/76087631
简单来讲,整体的js代码这个macrotask先执行,同步代码执行完后有microtask执行microtask,没有microtask执行下一个macrotask,如此往复循环至结束
-----------------------------------------------------------------------
我们知道 DOM 操作会触发浏览器对文档进行渲染,如修改排版规则,修改背景颜色等等,那么这类操作是如何在浏览器当中奏效的?至此我们已经知道了事件循环是如何执行的,事件循环器会不停的检查事件队列,如果不为空,则取出队首压入执行栈执行。当一个任务执行完毕之后,事件循环器又会继续不停的检查事件队列,不过在这间,浏览器会对页面进行渲染。这就保证了用户在浏览页面的时候不会出现页面阻塞的情况,这也使 JS 动画成为可能, jQuery 动画在底层均是使用 setTimeout 和 setInterval 来进行实现。想象一下如果我们同步的执行动画,那么我们不会看见任何渐变的效果,浏览器会在任务执行结束之后渲染窗口。反之我们使用异步的方法,浏览器会在每一个任务执行结束之后渲染窗口,这样我们就能看见动画的渐变效果了。
Several runtimes communicating together
A web worker or a cross-origin iframe
has its own stack, heap, and message queue. Two distinct runtimes can only communicate through sending messages via the postMessage
method. This method adds a message to the other runtime if the latter listens to message
events.
--------------------------------------------------------------------分割线--------------------------------------------------------------------------------
放个面试题,抛个砖:
console.log(\'start\')
const interval = setInterval(() => {
console.log(\'setInterval\')
}, 0)
setTimeout(() => {
console.log(\'setTimeout 1\')
Promise.resolve()
.then(() => {
console.log(\'promise 3\')
})
.then(() => {
console.log(\'promise 4\')
})
.then(() => {
setTimeout(() => {
console.log(\'setTimeout 2\')
Promise.resolve()
.then(() => {
console.log(\'promise 5\')
})
.then(() => {
console.log(\'promise 6\')
})
.then(() => {
clearInterval(interval)
})
}, 0)
})
}, 0)
Promise.resolve()
.then(() => {
console.log(\'promise 1\')
})
.then(() => {
console.log(\'promise 2\')
})
不着急揭晓答案,先分析
首先知晓:
js是单线程语言
也就是说一次就只能做一件事情。
多数的网站不需要大量计算,程序花费的时间主要集中在磁盘 I/O 和网络 I/O 上面
虽然SSD读取很快,但和CPU处理指令的速度比起来也不在一个数量级上,而且网络上一个数据包来回的时间更慢(注意过游戏的延迟吗)
so: 一些cpu直接执行的任务就成了优先执行主线任务,然后需要io返回数据的任务就成了等待被执行的任务
所以才会有同步任务(synchronous)和异步任务(asynchronous)之分
同步任务:
在主线程上排队执行的任务,前一个任务执行完毕,才能执行后一个任务;
异步任务:
不进入主线程、而进入”任务队列”(task queue)的任务,只有”任务队列”通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
总之:
只要主线程空了,就会去读取”任务队列”,这就是javascript的运行机制
Microtasks Macrotasks
任务队列不止一个,还有 microtasks 和 macrotasks
microtasks:
- process.nextTick
- promise
- Object.observe
- MutationObserver
macrotasks:
- setTimeout
- setInterval
- setImmediate
- I/O
- UI渲染
whatwg规范:https://html.spec.whatwg.org/multipage/webappapis.html#task-queue
- 一个事件循环(event loop)会有一个或多个任务队列(task queue)
- task queue 就是 macrotask queue
- 每一个 event loop 都有一个 microtask queue
- task queue == macrotask queue != microtask queue
- 一个任务 task 可以放入 macrotask queue 也可以放入 microtask queue 中
理解了这些定义之后,再看执行原理:
事件循环的顺序,决定了JavaScript代码的执行顺序。它从script(整体代码)开始第一次循环。之后全局上下文进入函数调用栈。直到调用栈清空(只剩全局),然后执行所有的micro-task。当所有可执行的micro-task执行完毕之后。循环再次从macro-task开始,找到其中一个任务队列执行完毕,然后再执行所有的micro-task,这样一直循环下去。
还要注意一点:
包裹在一个 script 标签中的js代码也是一个 task 确切说是 macrotask。
所以文首面试题的答案为:
start
promise 1
promise 2
setInterval
setTimeout 1
promise 3
promise 4
setInterval
setTimeout 2
promise 5
promise 6
简单来讲,整体的js代码这个macrotask先执行,同步代码执行完后有microtask执行microtask,没有microtask执行下一个macrotask,如此往复循环至结束
以上是关于javascript event loop的主要内容,如果未能解决你的问题,请参考以下文章
JavaScript 运行机制详解:Event Loop——续