F09第 1 / 1 课时未开始内容版本 1.0.0 · 核验于 2026-10-26

异步、Promise 与事件循环

异步、Promise 与事件循环 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。

学习目标

这一课用几段「时间可控」的小代码看清 JavaScript 的异步执行顺序,然后把串行、并行、失败处理、超时这四个武器配齐。读完你应该能预测一段含 setTimeout 和 Promise 的代码的输出顺序,并写出可靠的批量任务代码。

问题场景

你要同时抓取三个课程接口。第一版用 for 循环逐个 await,三个请求一个接一个,总耗时是三者之和;第二版听说要「并行」,改成 Promise.all,结果其中一个接口 404,三个结果全部作废。串行太慢,并行又怕一颗老鼠屎坏一锅粥——先搞懂事件循环,这些选择就不难了。

核心解释

JavaScript 是单线程的:同一时刻只执行一段代码。异步任务不是「同时做」,而是「排进队列稍后再做」,调度者叫事件循环(event loop,不断从队列里取任务执行的机制)。规则两条:

  1. 同步代码一口气跑完;
  2. 然后先清空微任务(microtask,Promise 回调所在的队列),再取一个宏任务(macrotask,比如 setTimeout 的回调);每取一个宏任务前后,都会先清空微任务。
console.log('1 同步');
setTimeout(() => console.log('4 宏任务'), 0);
Promise.resolve().then(() => console.log('3 微任务'));
console.log('2 同步');
// 输出顺序:1 2 3 4 —— 微任务插队,setTimeout(0) 也不是「立刻」

async/await 是 Promise 的语法糖:await 之后的代码相当于放进 then 的回调,也就是微任务。所以「在循环里 await 一串任务」是串行(一个完成才下一个);想要并行,先把 Promise 创建出来再一起 await:

// 串行:总耗时 ≈ a + b
const a = await fetchA();
const b = await fetchB();

// 并行:总耗时 ≈ max(a, b) —— 两个请求同时出发,一起等
const [ra, rb] = await Promise.all([fetchA(), fetchB()]);

示例拆解

失败处理按「能不能接受部分失败」选工具:

const results = await Promise.allSettled([fetchA(), fetchB(), fetchC()]);
const ok = results.filter((r) => r.status === 'fulfilled').map((r) => r.value);
const failed = results.filter((r) => r.status === 'rejected');
console.log(ok, failed); // 成功的照常用,失败的单独提示
  • Promise.all:一个失败立即整体拒绝,适合「缺一个就没意义」的场景,比如一次页面渲染的必需数据。
  • Promise.allSettled:每个任务都给结果,适合互相独立的任务,比如抓多个可互相替代的数据源。
  • 超时用 Promise.race(谁先结束就用谁的结果):让「任务」和「定时 reject 的闹钟」赛跑,闹钟先响就按超时处理,避免永远等下去。

动手任务

打开本课实验区(输出都带毫秒时间戳):

  1. 先在注释里写下实验区第一段代码的完整输出顺序,再运行对照——顺序错一个都算没过关。
  2. 把实验区里「串行抓两门课」改成并行,记录两种写法在控制台显示的总耗时差。
  3. 用 Promise.race 给并行抓取加 800ms 超时:超时时输出「请求超时」,而不是永远等下去。

常见错误

  • 在 for 循环里 await 还以为是并行:循环里 await 是串行;要并行先收集 Promise,再交给 Promise.all。
  • 以为 setTimeout(fn, 0) 立刻执行:它至少要等同步代码和全部微任务结束。
  • async 函数里 try/catch 漏掉 await:fetch(...) 不加 await 就不会把错误抛进 catch,异常处理形同虚设。
  • 创建了 Promise 却没人接:调用 async 函数返回的 Promise 没有 await 或 catch,失败会变成「未处理的 promise 拒绝」,控制台报错但代码继续跑。

检查与复盘

完成下方检查题确认理解。自查清单:

  • 能说出「同步 → 微任务 → 宏任务」的执行顺序并正确预测输出
  • 能为「必需数据」和「可缺数据」分别选对 all 和 allSettled
  • 知道串行与并行的耗时差别,并且亲手测过一次

参考资料

动手实验
动手实验
预览
点击「运行」在隔离沙箱中预览
检查题共 3 题,提交后立即显示解析
答案由服务端判定;答错的题会自动进入复习计划。
Q1读代码较难

阅读代码(浏览器环境,普通页面脚本)。控制台输出顺序是?

console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => {
  console.log('3');
  setTimeout(() => console.log('4'), 0);
});
console.log('5');
Q2多选较难(多选,全部选对才算正确)

关于浏览器里的任务调度,下列说法正确的有哪些?(多选)

Q3单选中等

需求:同时请求 3 个接口,要求「全部结束后拿到每个请求的成功或失败结果,失败的位置用占位数据顶上」。应该用哪个 API?

读完这一课了?

标记完成后,工作台会安排接下来的学习。

Command Palette

Search for a command to run...