Skip to content

Latest commit

 

History

History
98 lines (81 loc) · 3.57 KB

File metadata and controls

98 lines (81 loc) · 3.57 KB

Event Loop

介绍

JavaScript 之所以被设计为单线程,是因为作为一门脚本语言,使用多线程会使浏览器变得非常复杂。比如,当一个线程正在修改一个 dom 元素的样式时,另一个线程将其删除。这样,浏览器就无法判断该怎么去执行。 Javascript 有一个 main thread 主线程和 call-stack 调用栈(执行栈),所有的任务都会被放到调用栈等待主线程执行。 Event Loop是一个程序结构,用于等待和发送消息及事件。在JavaScript中,任务被分为MacroTask(宏任务)和MicroTask(微任务)两种。

  • MacroTask: script(整体代码), setTimeout, setInterval, setImmediate(node、IE10), I/O, UI rendering
  • MicroTask: process.nextTick(node), Promises, Object.observe(废弃), MutationObserver

一次事件循环:先执行 macroTask 的所有同步任务,执行栈清空后,查看有没有 microTask,有的话一次性执行所有的微任务,接着开始下一次循环。

运行顺序

运行 script(MacroTask)时,会先执行内部所有的同步任务,执行完后开始检查 MicroTask 。如果有 process.nextTick 会插入执行栈最后(优先于其他 MicroTask),如果有 setImmediate 则会滞后于其他 MicroTask。

Node的Event Loop分阶段,阶段有先后,依次是

  • expired timers and intervals,即到期的setTimeout/setInterval
  • I/O events,包含文件,网络等等
  • immediates,通过setImmediate注册的函数
  • close handlers,close事件的回调,比如TCP连接断开

同步任务及每个阶段之后都会清空microtask队列

优先清空next tick queue,即通过process.nextTick注册的函数 再清空other queue,常见的如Promise

console.log(1)

setTimeout(() => { // MacroTask
  console.log(2)
}, 0)

Promise.resolve().then(() => {  // MicroTask
	console.log(3)
}).then(() => {
	console.log(4)
})

console.log(5)

// 1、5、3、4、2

console.log(1)

setTimeout(() => {
    console.log(2)
    new Promise(resolve => {
        console.log(4)
        resolve()
    }).then(() => {
        console.log(5)
    })
    process.nextTick(() => {
        console.log(3)
    })
})

new Promise(resolve => {
    console.log(7)
    resolve()
}).then(() => {
    console.log(8)
})

process.nextTick(() => {
    console.log(6)
})
// 1、7、6、8、2、4、3、5

setTimeout(function() {
    console.log('setTimeout');
}, 3000)

new Promise(function(resolve) {
    setTimeout(function() {
        console.log('promise');
        resolve()
    }, 2000)
}).then(function() {
    console.log('then');
})

console.time()
for (let i = 0; i < 20000; i++) {
    setTimeout(function() {
        console.log('for');
    }, 3000)
    // console.log('for');
}
console.timeEnd()
console.log('console');
// 执行完所有同步任务,观察有没有微任务,没有再进行下一个宏任务(setTimeout的计时是同时进行的,但是就算时间到了,也一定要执行栈清空以后才会运行。而且,被阻塞后,就算所有的计时器都到时间了,依然会按照定时器到点的先后顺序进行执行)。

setTimeout

浏览器页面是由消息队列和事件循环系统来驱动的。

  • 如果当前同步任务执行时间过久,会影延迟到期定时器任务的执行
  • 如果 setTimeout 存在嵌套调用,那么系统会设置最短时间间隔为 4 毫秒
  • 未激活的页面,setTimeout 执行最小间隔是 1000 毫秒。为了优化后台页面的加载损耗以及降低耗电量。
  • 延时执行时间有最大值,不能大于 2147483647 毫秒(大约 24.8 天)。