事件循环(Event Loop)练习题

事件循环(Event Loop)练习题,帮助你巩固对宏任务、微任务和异步执行顺序的理解。每个题目都附有详细解析:

题目1:混合 Promise 和 setTimeout

console.log('Start');

setTimeout(() => console.log('Timeout 1'), 0);

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

Promise.resolve().then(() => console.log('Promise 2'));

console.log('End');

<details>
<summary>查看答案与解析</summary>

输出顺序:

Start
End
Promise 1
Promise 2
Timeout 1
Timeout 2

解析:

  1. 同步代码:StartEnd
  2. 微任务队列:
    • 第一个 Promise:输出 Promise 1,添加新的宏任务(Timeout 2)
    • 第二个 Promise:输出 Promise 2
  3. 宏任务队列:
    • 执行第一个 setTimeout:输出 Timeout 1
    • 执行第二个 setTimeout:输出 Timeout 2
      </details>

题目2:多层异步嵌套

console.log('Script start');

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

Promise.resolve().then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
  setTimeout(() => console.log('inner setTimeout'), 0);
});

console.log('Script end');

<details>
<summary>查看答案与解析</summary>

输出顺序:

Script start
Script end
promise1
promise2
setTimeout
inner setTimeout

解析:

  1. 同步代码:Script startScript end
  2. 微任务队列:
    • 第一个 then:输出 promise1,返回新 Promise
    • 第二个 then:输出 promise2,添加新宏任务
  3. 宏任务队列:
    • 第一个 setTimeout:输出 setTimeout
    • 第二个 setTimeout:输出 inner setTimeout
      </details>

题目3:async/await 与 Promise 混合

async function async1() {
  console.log('A');
  await async2();
  console.log('B');
}

async function async2() {
  console.log('C');
  await new Promise(resolve => {
    console.log('D');
    resolve();
  });
  console.log('E');
}

console.log('F');

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

async1();

new Promise(resolve => {
  console.log('H');
  resolve();
}).then(() => console.log('I'));

console.log('J');

<details>
<summary>查看答案与解析</summary>

输出顺序:

F
A
C
D
H
J
E
B
I
G

解析:

  1. 同步代码:FACDHJ
  2. 微任务队列:
    • async2 的 await:输出 E
    • async1 的 await:输出 B
    • Promise 的 then:输出 I
  3. 宏任务:G
    </details>

题目4:复杂微任务链

console.log('1');

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

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

Promise.resolve()
  .then(() => console.log('6'))
  .then(() => console.log('7'));

console.log('8');

<details>
<summary>查看答案与解析</summary>

输出顺序:

1
8
3
6
4
7
5
2

解析:

  1. 同步代码:18
  2. 微任务队列:
    • 第一个 Promise 链:345
    • 第二个 Promise 链:67
    • 注意:return Promise.resolve 会创建额外的微任务
  3. 宏任务:2
    </details>

题目5:事件循环综合题

console.log('Start');

document.addEventListener('click', () => {
  console.log('Click');
  Promise.resolve().then(() => console.log('Microtask in Click'));
});

setTimeout(() => {
  console.log('Timeout');
  Promise.resolve().then(() => console.log('Microtask in Timeout'));
}, 0);

Promise.resolve().then(() => console.log('Promise 1'));

console.log('End');

<details>
<summary>查看答案与解析</summary>

初始输出顺序(不触发点击):

Start
End
Promise 1
Timeout
Microtask in Timeout

如果触发点击事件:

Start
End
Promise 1
Timeout
Microtask in Timeout
Click
Microtask in Click

解析:

  1. 同步代码:StartEnd
  2. 微任务:Promise 1
  3. 宏任务:Timeout 及其微任务 Microtask in Timeout
  4. UI事件(点击)作为宏任务处理
  5. 每个宏任务后都会清空微任务队列
    </details>

解题技巧总结:

  1. 同步代码总是最先执行
  2. 微任务(Microtask)执行时机
    • 在每个宏任务之后
    • 在DOM渲染之前
    • 清空整个微任务队列(包括嵌套产生的)
  3. 宏任务(Macrotask)执行时机
    • 一次事件循环只执行一个宏任务
    • 包括:setTimeout、setInterval、I/O、UI渲染、事件回调
  4. async/await 本质
    • await 之前的代码是同步的
    • await 之后的代码相当于 .then() 回调
  5. Promise 链
    • 每个 .then() 都会创建新的微任务
    • return Promise 会创建额外的微任务

建议你尝试自己分析这些题目,画出事件循环的流程图,然后对照解析验证理解。掌握这些模式后,你就能准确预测任何JavaScript异步代码的执行顺序了!

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容