当前位置: 代码迷 >> 综合 >> javascript基础-微任务、宏任务、event loop
  详细解决方案

javascript基础-微任务、宏任务、event loop

热度:39   发布时间:2023-12-11 23:23:55.0

js任务分同步任务和异步任务。

在异步任务中,分为宏任务 macro 和微任务 micro。

通常微任务的执行是在当前主线程宏任务执行完毕之前,当前没有可执行的微任务时,才会开启下一个宏任务。

一、微任务

微任务包含 async、Promise.then、MutationObserver、webworker。

微任务不按照代码执行顺序执行,但他是在当前宏任务执行结束前都会执行完;可以获取回调。

nodeJs中的process.nextTick,也是微任务。

二、宏任务

宏任务包含 setTimeout、浏览器事件、新的script 标签、nodeJs事件(setImmediate、process.nextTick)、ajax、requestAnimationFrame、postMessage、MessageChannel。

宏任务不按照代码执行顺序执行,可以看成了拉了一个新的执行队列;没有回调。

三、event loop

event loop解决的问题是,实现一个异步队列,将比较耗时的任务放到异步队列,等主线程里的任务执行完再执行这个异步队列,执行完后将线程占用权转交给主线程。

触发异步队列的条件是,主线程执行完成后。

异步队列的执行顺序是,先进先出。

一、浏览器中的 event loop

一个script标签就是一个宏任务,所以宏任务队列是率先作为主线程的。在这个宏任务中如果有诸如 promise 、mutationObserver 之类的微任务,那么会将微任务延迟到主线程执行完执行。然后执行下一个宏任务。

二、nodeJs 中的 event loop

nodeJs 中的代码会先进过 v8 引擎编译,然后通过 nodeJs api 操作 libuv(基于 event loop 实现),将所有的任务交给一个工作线程。

当某一个工作完成时,会触发一个 callback,将线程控制权返还给应用程序。

libuv 的 event loop 分为几个阶段:

  1. 定时器阶段 timeout 等
  2. 循环迭代、io 回调
  3. 内部钩子函数
  4. poll 阶段,不断寻找新的事件循环或新的工作完成
  5. check 阶段,执行 setImmdiate

对于同一个线程内,先注册 setImmediate 后 注册 setTimout,为什么可能出现 setImmediate 比 setTimeout 先执行的情况?

因为可能在执行 setImmediate 时,setTImeout 还未被 libuv 捕获。

同样的,一堆 setTimeout 0 之前 写 setTimout 1 也可能出现这种情况。

libuv 的 poll 阶段有明显的“连续性”,如果在一个异步任务中同时创建 setImmediate 和 setTimout,那么 setImmediate 会先执行,这是因为 libuv 的异步阶段执行到了poll 阶段,setTImout 还没来的及到期,poll 阶段的下一个阶段 check 阶段就要执行 setImmediate 了。

setTimeout(() => {setTimeout(() => {console.log(1);});setImmediate(() => {console.log(0);});
});
setTimeout(() => {console.log(1);
});
setImmediate(() => {console.log(0);
});
//  1 0 0 1

nodeJs 中的微任务 process.nextTick,是优先级最高的微任务,会介于 event loop 的阶段切换之间执行。

v11以前的宏任务和微任务执行流程:

优先级相同(同一个线程内注册的)的宏任务(内的主线程代码)会都执行完,才执行它们对应的微任务。

v11以后的宏任务和微任务执行流程:

宏任务内创建的微任务,会在另一个宏任务执行前执行。已经和浏览器类似了。

  相关解决方案