Skip to main content

事件循环

事件循环是什么

JavaScript是一门单线程的语言,意味着同一时间只能做一件事,但是这并不意味着单线程就是阻塞,而实现单线程非阻塞的方法就是事件循环 在javascrip中,所有的任务都可以分为

  • 同步任务:立即执行的任务,同步任务一般会直接进入到主线程中执行
  • 异步任务:异步执行的任务,比如ajax网络请求,setTimeout定时函数等;异步任务还可以细分为微任务与宏任务

微任务与宏任务

执行机制:

  • 执行一个宏任务,如果遇到微任务就将它放到微任务的事件队列中
  • 当前宏任务执行完成后,会查到微任务的事件队列,然后将里面的所有微任务依次执行完

微任务

一个需要异步执行的函数,执行时机就是在主函数执行结束之后、在当前宏任务结束之前

常见的微任务有:

  • Promise.then
  • MutaionObserver
  • Object.observe(已废弃;Proxy对象替代)
  • process.nextTick(Node.js)
  • asyncawait阻塞

宏任务

宏任务的时间粒度比较大,执行的时间间隔是不能精确控制的,对一些高实时性的需求就不太符合

常见的宏任务有:

  • script(外层同步代码)
  • setTimeout/setInterval
  • UI rendering/UI事件
  • postMessageMessageChannel
  • setImmediate、I/O(Node.js)
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2')
}
console.log('script start')
setTimeout(function () {
console.log('settimeout')
})
async1()
new Promise(function (resolve) {
console.log('promise1')
resolve()
}).then(function () {
console.log('promise2')
})
console.log('script end')
note
  1. 执行整段代码,遇到 console.log('script start') 直接打印结果,输出 script start
  2. 遇到定时器了,它是宏任务,先放着不执行
  3. 遇到 async1(),执行 async1 函数,先打印 async1 start,下面遇到await怎么办?先执行 async2,打印 async2,然后阻塞下面代码(即加入微任务列表),跳出去执行同步代码
  4. 跳到 new Promise 这里,直接执行,打印 promise1,下面遇到 .then(),它是微任务,放到微任务列表等待执行
  5. 最后一行直接打印 script end,现在同步代码执行完了,开始执行微任务,即 await下面的代码,打印 async1 end
  6. 继续执行下一个微任务,即执行 then 的回调,打印 promise2
  7. 上一个宏任务所有事都做完了,开始下一个宏任务,就是定时器,打印 settimeout

所以最后的结果是:script startasync1 startasync2promise1script endasync1 endpromise2settimeout