Skip to content

事件循环

浏览器的事件处理机制&运行机制,称 EventLoop

为什么要有事件循环机制

JS 线程和渲染线程互斥切互相阻塞(同步获取 DOM 信息),为了避免 GUI 的卡顿,让异步操作同步执行,引入了浏览器事件循环机制 js 在单线程情况下非阻塞的调用网络,定时器等异步 API

什么是:运行机制

  • 同步任务在主线程上执行,形成执行栈,先进后出
  • 异步任务有了运行结果,就在任务队列之中放一个事件:形成任务队列, 先进先出,不同的异步任务会分配到不同的队列中

微任务&宏任务:

它分为微任务和宏任务,它放在任务队列中

  • 任务源可以分为 微任务和宏任务
  • 微任务包括 promise process.nextTick MutationObserver
  • 宏任务 script setTimeout setInterval setImmediate I/O UI rendering requestAnimationFrame

他们是有先后顺序的

  • 宏任务 - 微任务 - 渲染 - 宏任务 - 微任务 - 渲染

  • 微任务 - 渲染 - 宏任务 - 微任务 - 渲染

  • 首先执行同步代码,这属于宏任务

  • 执行栈为空,查询是否有异步任务

  • 执行微任务

  • 如有必要渲染页面

  • 下一轮

为什么区分微任务和宏任务

为了事情的优先级,宏任务一般耗时比较长,微任务耗时相对较短

setTimeout Promise

js
setTimeout(() => {
  console.log(1);
}, 0);
Promise.resolve().then(() => {
  console.log(2);
});
console.log(3);

// 3 2 1
// main script 运行结束后,会有微任务队列和宏任务队列。
// 微任务先执行,之后是宏任务。

setTimeout promise

js
setTimeout(() => {
  console.log(1);
}, 0);
let a = new Promise((resolve) => {
  console.log(2);
  resolve(3);
})
  .then(() => {
    console.log(3);
    return 3;
  })
  .then(() => {
    console.log(4);
    return 4;
  });
console.log(5);

// 2 5 3 4 1

Promise 有 return

js
new Promise((resolve, reject) => {
  console.log("promise1");
  resolve(); // 异步让出 向下执行
})
  .then(() => {
    console.log("then11");
    return new Promise((resolve, reject) => {
      console.log("promise2");
      resolve(); // 异步向下执行
    })
      .then(() => {
        console.log("then21"); // 然后在then内部先执行 ,在resolve 异步让出向下
      })
      .then(() => {
        console.log("then23");
      });
  })
  .then(() => {
    console.log("then12"); //
  });
// promise1 -> then11 -> promise2 -> then21 -> then23 -> then12

promise 嵌套无 return

js
new Promise((resolve, reject) => {
  console.log("promise1");
  resolve();
})
  .then(() => {
    console.log("then11");
    new Promise((resolve, reject) => {
      console.log("promise2");
      resolve();
    })
      .then(() => {
        console.log("then21");
      })
      .then(() => {
        console.log("then23");
      });
  })
  .then(() => {
    console.log("then12");
  });
// promise1 -> then11 -> promise2 -> then21 -> then12 -> then23

多 promise [同层级,顺序为:从外到里]

js
new Promise((resolve, reject) => {
  console.log("promise1");
  resolve();
})
  .then(() => {
    console.log("then11");
    new Promise((resolve, reject) => {
      console.log("promise2");
      resolve();
    })
      .then(() => {
        console.log("then21");
      })
      .then(() => {
        console.log("then23");
      });
  })
  .then(() => {
    console.log("then12");
  });

new Promise((resolve, reject) => {
  console.log("promise3");
  resolve();
}).then(() => {
  console.log("then31");
});

// promise1 -> promise3 -> then11 -> promise2 -> then31 -> then21 -> then12 -> then23

async await promise

js
async function async1() {
  console.log("async1 start");
  await async2();
  console.log("async1 end");
}
async function async2() {
  console.log("async2");
}
async1();
// 用于test的promise,看看await究竟在何时执行
new Promise(function (resolve) {
  console.log("promise1");
  resolve();
})
  .then(function () {
    console.log("promise2");
  })
  .then(function () {
    console.log("promise3");
  })
  .then(function () {
    console.log("promise4");
  })
  .then(function () {
    console.log("promise5");
  });
// async1 start -> async2 -> promise1 -> async1 end -> promise2 -> promise3 -> promise4 -> promise5

// async/await有时候会推迟两轮 microtask,
// 在第三轮 microtask 执行,主要原因是浏览器对于此方法的一个解析,由于为了解析一个 await,要额外创建两个 promise,因此消耗很大。
// 后来 V8 为了降低损耗,所以剔除了一个 Promise,并且减少了 2 轮 microtask,所以现在最新版本的应该是“零成本”的一个异步。

多 async

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");

// script start -> async1 start -> async2 -> promise1
// script end -> async1 end -> promise2 -> settimeout

在 MIT 许可下发布