JavaScript事件循环【1】

发布于:2023-01-13 ⋅ 阅读:(506) ⋅ 点赞:(0)

其实JS中的事件循环还有很多细节的东西,setImmediate、nexttick、Promise A+规范等等,有深挖的价值,这一篇旨在介绍事件循环的概念以及简单地应用。

1 什么是事件循环

JavaScript是单线程语言,意味着同一时间只能做一件事情,但不等同于阻塞,而非阻塞单线程的一个实现方法就是事件循环模型。

在JavaScript中我们把所有代码分为异步代码和同步代码:

同步任务:直接在主线程执行。
异步任务:注册异步任务,当满足条件(如定时器到时,Promise被resolve等等)时推入异步任务队列。

在主线程代码执行完毕之后进行如下操作:

1、查看异步任务队列中是否存在未执行完毕的任务,如果有,则取出一个推入主线程执行
2、待主线程将该任务执行完毕后,继续查看异步任务队列.....(不断循环直到清空任务队列)。

2 宏任务微任务

按照这个思路来理解下面的代码。

console.log('start')
setTimeout(() => {
  console.log('timer1');
  Promise.resolve().then(() => {
    console.log('promise')
  })
}, 0);
setTimeout(() => {
  console.log('timer2')
}, 0);
console.log('end')
  • 开始执行,异步队列为 [ ]
  • console.log("start"),同步代码直接执行,输出 start 
  • setTimeout(),记timer1,异步任务,放入异步任务队列,此时异步队列 [ timer1 ]
  • setTimeout(),记timer2,异步任务,同样放入队列,此时异步队列 [ timer1, timer2 ]
  • console.log("end"),同步代码直接执行,输出 end
  • ---------------------------------------------------------------------------------------------------------------------
  • 执行完毕,查找队列,取出 timer1,异步队列 [ timer2 ]
  • 执行 timer1 内部代码,输出 tiemr1 。then(),异步任务,加入队列,异步队列 [ timer2, then ]
  • 执行完毕,查找队列,取出 timer2,异步队列 [ then ]
  • 执行 timer2 内部代码,输出 timer2
  • 执行完毕,查找队列,取出 then,执行,输出 promise
  • 队列清空,执行完毕。

分析后,输出结果为 start -> end -> timer1 -> timer2 -> promise 

但实际上我们的输出结果为 start -> end -> timer1 -> promise -> timer2

原因在于异步任务根据时间粒度的大小被划分为了宏任务和微任务。简单概括为 “在执行下一个宏任务之前,清空微任务队列”。

常见的宏任务:

  • script(整体代码)
  • setTimeout/setInterval
  • postmessage
  • messagechannel
  • I/O(NodeJs)
  • setImmediate(NodeJs)
  • UI交互
  • ...

常见的微任务:

  • Promise.then
  • MutationObserver
  • process.nextTick(NodeJs)
  • ...

当我们把异步任务分为宏任务和微任务后,代码的执行流程如下图所示:

  • 执行一个宏任务,如果遇到微任务就将它放到微任务的事件队列中
  • 当前宏任务执行完毕,查看微任务队列将队列中所有微任务依次执行完毕
  • 执行下一个宏任务

网站公告

今日签到

点亮在社区的每一天
去签到