事件循环(event loop)介绍

21 阅读1分钟

经典执行顺序题

JS是单线程:同一时间只能做一件事情。

但是有些任务是耗时的,会阻塞代码的执行

因此,将代码分为同步代码和异步代码

同步代码立刻放进JS引起执行,原地等待结果。

异步代码放入宿主环境,不阻塞主线程,主线程继续执行代码,异步执行的结果将在将来执行

时间一到,宿主环境将回调函数推送到任务队列,任务队列推送到执行栈

整个是一个循环的过程,将会不断检测执行站和任务队列。

image.png

顺序: 同步代码,微任务入栈,宏任务入栈

原则:先进先出,将所有微任务入栈,执行完后再检查一遍有没有微任务,清空微任务后再执行宏任务

console.log('1');

setTimeout(() => {
  console.log('2');
}, 0);

new Promise((resolve) => {
  console.log('3');
  resolve();
}).then(() => {
  console.log('4');
});

console.log('5');

// 问:输出顺序是什么?
```

<details>
<summary>答案</summary>
```
1
3
5
4
2

解析:

  • console.log('1') - 同步代码
  • setTimeout - 宏任务,放入宏任务队列
  • new Promise - 构造函数同步执行,输出 3
  • .then() - 微任务,放入微任务队列
  • console.log('5') - 同步代码
  • 微任务先执行:输出 4
  • 宏任务后执行:输出 2

async/await 与 Promise 混合

await要阻塞等待其执行 await后面的代码算微任务

async function async1() {
  console.log('async1 start');
  await async2();
  console.log('async1 end');
}

async function async2() {
  console.log('async2');
}

console.log('script start');

setTimeout(() => {
  console.log('setTimeout');
}, 0);

async1();

new Promise(resolve => {
  console.log('promise1');
  resolve();
}).then(() => {
  console.log('promise2');
});

console.log('script end');

// 问:输出顺序?
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout

解析:

  • await async2() 相当于 Promise.resolve(async2()).then(() => console.log('async1 end'))
  • await 后面的代码被放入微任务队列