事件循环,宏任务与微任务

February 22, 2022

JavaScript是一个单线程的脚本语言,所以在一行代码执行的过程中,必然不会存在同时执行的另一行代码。

如果有些代码执行了大量的计算,比方在前端破解密码之类的操作,就会导致后续代码一直在等待,页面处于假死状态,因为前边的代码没有执行完。

所以如果所有代码都是同步执行的,对于一些请求难道要一直循环代码去判断是否拿到了返回结果么,于是就有异步事件的概念。注册一个回调函数,比如说发一个网络请求,我们告诉主程序等到接收到数据后通知我,然后我们就可以去做其他事情了。

然后在异步完成后,会通知我们,但是此时可能程序正在做其他事情,所以即使异步完成了也要在一旁等待,等到程序空闲下来才有时间去看哪些异步已经完成了,可以去执行。

微任务和宏任务的区别

多个宏任务合在一起可以认为有一个任务队列在这,任务队列中的都是已经完成的异步操作,在当前的微任务没有执行完成时,是不会执行下一个宏任务的。

  • 两者都是异步
  • 进程的切换肯定是宏任务,因为需要花费大量的资源
  • 线程的切换是微任务,只需要在一个进程中切换,更确切地说,微任务时纤程的切换导致的,纤程是比线程更小的概念
  • 为什么跟定时器有关的任务是宏任务? 因为计时器是实时的,不能被阻塞,所以定时器被设计在另一个进程中被管理,所以定时器任务会有进程的切换。
  • 事件为什么是宏任务? 事件的触发依赖于浏览器的实现,平台有其自己的事件注册和派发机制,事件的管理中心也一定是在另外一个进程中实现的。

宏任务

  • script整体代码
  • setTimeout
  • setInterval
  • requestAnimationFrame
  • I/O

微任务

  • Promise.then catch finally
  • mutationObserver

什么是事件循环

JavaScipt代码执行的过程中,除了依靠函数调用栈来确定函数的执行顺序外,还依靠任务队列来确定代码执行,整个执行过程称为事件循环过程。一个线程中,事件循环是唯一的,但是任务队列可以拥有多个。任务队列又分为宏任务与微任务。

所有的任务可以分为同步任务和异步任务。同步任务就是立即执行的任务,而异步任务就是异步执行的任务。异步任务会通过任务队列的机制来进行协调。每个宏任务执行完,都会检查有没有微任务,如果有,执行完所有的微任务再执行下一个宏任务。

浏览器的事件循环

浏览器的事件循环由一个宏任务队列+多个微任务队列组成。

首先,执行第一个宏任务:全局script脚本,产生的宏任务和微任务进入各自的队列中。执行完script后,把当前的微任务队列清空,完成一次事件循环。

接着再取出一个宏任务,同样把在此期间产生的回调入队,再把当前的微任务队列清空。

宏任务队列只有一个,而每一个宏任务都有一个自己的微任务队列, 每轮循环都是由一个宏任务+多个微任务组成。

Promise.resolve().then(()=>{
  console.log('第一个回调函数:微任务1')  
  setTimeout(()=>{
    console.log('第三个回调函数:宏任务2')
  },0)
})
setTimeout(()=>{
  console.log('第二个回调函数:宏任务1')
  Promise.resolve().then(()=>{
    console.log('第四个回调函数:微任务2')   
  })
},0)
// 第一个回调函数:微任务1
// 第二个回调函数:宏任务1
// 第四个回调函数:微任务2
// 第三个回调函数:宏任务2

Profile picture

百事可乐

Let it snow, let it snow, let it snow