JavaScript之事件循环

549 阅读2分钟

事件循环

JavaScript 是单线程语言,有一个主线程 main thread和调用栈 call-stack(执行栈),所有的任务都会放到调用栈中等待主线程来执行。

事件循环Event Loop是实现异步的一种方法,也是js的执行机制:

  • js执行过程中,同步和异步任务分别进入不同的“场所”,同步任务进入主线程,自上而下执行,异步任务进入Event Table 并注册对应的回调函数。
  • 异步任务完成后,Event Table 会将这个函数移入 Event Queue
  • 主线程任务执行完了以后,会从Event Queue中读取任务,进入到主线程去执行
  • 循环上述过程,就是Event Loop

setTimeout

setTimeout是异步的,可以延迟执行,是指经过指定时间后,把要执行的任务加入到Event Queue中,并不立即执行

因此,当主线程的函数执行时间过长,setTimeout就只能等着,因此执行时间可能会大于设置的时间。

即使setTimeout(fn,0),0秒也不会立即执行,需要等待主线程的同步任务执行完毕。

setInterval

setInterval是循环执行,每隔指定的时间将注册的函数放入Event Queue,等待执行。

对于setInterval(fn,ms),一旦setInterval的回调函数fn执行时间超过了延迟时间ms,那么就完全看不出来有时间间隔了。

宏任务和微任务

  • macro-task(宏任务):包括整体代码script,setTimeout,setInterval
  • micro-task(微任务):Promise(处理异步的一种方法),process.nextTick(callback)(在事件循环的下一次中调用 callback 回调函数)

不同类型的任务会进入相对应的Event Queue,事件循环的顺序,决定js代码的执行顺序

进入整体代码(宏任务)后,开始第一次事件循环,执行所有微任务,然后再从宏任务开始执行下一次循环。

setTimeout(function() {
    console.log('setTimeout');
})
new Promise(function(resolve) {
    console.log('promise');
    resolve();
}).then(function() {
    console.log('then');
})
console.log('console');
  • 整段代码作为宏任务,进入主线程
  • setTimeout将回调函数注册后放到宏任务的Event Queue
  • Promise(微任务)立即执行,then函数放到微任务Event Queue
  • console.log立即执行,整段代码的宏任务执行完毕,开始执行微任务Event Queue里面的then函数
  • 到此,整段代码的第一次事件循环完毕
  • 第二轮循环从宏任务的Event Queue开始,执行setTimeout的回调函数

因此,输出为

promise
console
then
setTimeout

总结

JavaScript 是自上而下执行任务,会遇到立马就要执行的任务和待会再执行的任务。对于那待会再执行的任务等到能执行了,也不会立即执行,你得等js 执行完这一趟才行。

相关阅读:juejin.cn/post/684490…