学习event loop 事件循环

184 阅读2分钟

浏览器event loop 事件循环

浏览器线程

浏览器是多进程的,包含Browser Process (管理浏览器tab等)、Render Process 渲染进程还有其他的进程,Render Process分为主线程,js引擎和 ui渲染线程

宏任务

宏任务 也称为 macrotasktasks

以下异步任务的回调会进入到MacrotaskQueue宏任务队列,包含

  1. setTimeOut
  2. setInteval
  3. setImmediate[Node环境]
  4. requestAnimationFrame[浏览器环境]
  5. I/O
  6. Ui rednder[浏览器环境]

执行顺序:ui rednder>requestAnimationFrame>setTimeOut>setInteval>setImmediate

I/O操作一般是耗时的,所以看返回的时机插入在谁后面执行,和定时的一样。

微任务

微任务 也称为 microtaskjobs

以下异步任务的回调会进入到MacrotaskQueue微任务队列,包含

  1. Process.nextTick[Node环境]
  2. Promise.then()
  3. Object.observer
  4. MutationObserver[浏览器环境]

执行顺序:Process.nextTick>Promise>MutationObserver

注意:Promise构造函数内的代码同步执行

浏览器端的事件循环机制

执行栈(调用栈)js stack、事件队列(宏任务队列)tasks、Marotask Queue、微任务队列Mirotask Queue

每次事件循环先从事件队列中取事件执行,其中有微任务,就放在微任务队列中,本次事件执行结束前去检查微任务队列,然后执行微任务队列中的方法,执行完成后结束本次循环。然后进行下次循环。

猜测setTimeOutsetInteval是计时排序执行,定时线程计时完成后推到事件任务队列中,I/O应该也是一样的

例子:

console.log("1");

setTimeout(function () {
  console.log("2");

  new Promise(function (resolve) {
    console.log("3");
    resolve();
  }).then(function () {
    console.log("4");
  });
});

new Promise(function (resolve) {
  console.log("5");
  resolve();
}).then(function () {
  console.log("6");
  new Promise(function (resolve) {
    console.log("7");
    resolve();
  }).then(function () {
    console.log("8");
  });
  setTimeout(function () {
    console.log("9");
  });
});
new Promise(function (resolve) {
  console.log("10");
  resolve();
}).then(function () {
  console.log("11");
});
setTimeout(function () {
  console.log("12");
  new Promise(function (resolve) {
    console.log("13");
    resolve();
  }).then(function () {
    console.log("14");
  });
}, 5);
setImmediate(() => {
  console.log("15");
});
setTimeout(function () {
  console.log("16");
}, 3);
setTimeout(function () {
  console.log("17");
}, 10);

输出结果:1 5 10 6 7 11 8 2 3 4 15 9 16 12 13 14 17

结果解析:

第一次循环 1 5 10 6 7 11 8

第二次循环 2 3 4

第三次循环 15

第四次循环 9

第五次循环 16

第六次循环 12 13 14

第七次循环 17

相当于是每2个宏任务的间隙去执行微任务