前置知识: JavaScript

Promise静态方法

67 minIntermediate2026/6/14

Promise静态方法详解:allSettled、any、withResolvers及传统方法对比。

Promise 静态方法(Promise Static Methods)

本篇对标 MIT 6.005(Software Construction)、Stanford CS110L(Safety in Systems Programming)与 CMU 15-440(Distributed Systems)教学水准,系统讲授 JavaScript Promise 的形式语义、静态方法族(Promise.all / Promise.allSettled / Promise.any / Promise.race / Promise.withResolvers)、组合子数学性质与工程化应用。所有数学公式使用 KaTeX 渲染,参考文献采用 ACM Reference Format。


1. 学习目标(Learning Objectives)

本节依据 Bloom 分类法(Bloom’s Taxonomy,Anderson & Krathwohl, 2001)组织六层认知目标。完成本篇后,学习者应能在各认知层级达成如下目标。

1.1 Remember(记忆)

  • R1:准确复述 Promise 的三态状态机(pending → fulfilled / rejected),列出状态转换的不可逆性(irreversibility)规则。
  • R2:列出 Promise 的五个核心静态方法(Promise.all / Promise.allSettled / Promise.any / Promise.race / Promise.withResolvers)的签名与返回值结构。
  • R3:背诵 Promise.allSettled 返回值的标准结构 { status: 'fulfilled', value }{ status: 'rejected', reason },以及 Promise.any 失败时抛出的 AggregateError 结构。

1.2 Understand(理解)

  • U1:解释 Promise 的”thenable”协议(thenable protocol),能引用 Promises/A+ 规范 §2.3 的”Promise Resolution Procedure”。
  • U2:阐述 Promise.all 的”fail-fast”(短路失败)语义与 Promise.allSettled 的”wait-all”(全等待)语义的形式区别。
  • U3:推演 Promise.withResolvers()(ES2024)如何解决”deferred pattern”中 resolve / reject 必须在 executor 内提取的代码异味(code smell)。

1.3 Apply(应用)

  • A1:在并发请求场景中正确选择 Promise.all / Promise.allSettled / Promise.any / Promise.race,给出基于业务语义(all-required / best-effort / first-success / first-settled)的决策矩阵。
  • A2:运用 Promise.withResolvers() 实现可取消的异步任务(cancellable task)、Promise 缓存(promise cache)、事件转 Promise(event-to-promise)等模式。
  • A3:实现一个支持并发限制(concurrency limit)、重试(retry)、超时(timeout)的批量请求调度器,处理 1000+ URL 列表且不压垮目标服务器。

1.4 Analyze(分析)

  • An1:对比 Promise.anyPromise.race 在失败处理上的语义差异——前者忽略拒绝直至全部失败,后者首次 settle 即结束,分析二者在”多源竞速”场景下的适用边界。
  • An2:拆解 Promise.withResolvers() 的规范定义(ECMA-262 §27.2.4.5),分析其等价于 new Promise((resolve, reject) => { return { promise, resolve, reject } }) 的执行轨迹。
  • An3:解构 Promise.all 的实现原理,分析其在 Iterable 输入下的同步迭代与异步填充数组机制。

1.5 Evaluate(评价)

  • E1:评估”Promise 链 vs async/await”两种异步风格在错误处理(try/catch vs .catch)、调试栈追踪(stack trace)、可读性上的权衡,引用 V8 团队 2017 年《Faster async functions and promises》。
  • E2:判断何时应使用 Promise.allSettled 替代 Promise.all,给出基于”部分失败可接受性”的决策准则。
  • E3:批判性分析”Promise 静态方法可完全替代手写并发控制库”的论断,引用 Bluebird / p-limit / p-queue 等库的不可替代能力。

1.6 Create(创造)

  • C1:设计一个通用的 Promise 组合子(combinator)库,提供 map / filter / reduce / partition / forEach 等函数式操作,对标 Scala Future 与 Haskell Async。
  • C2:实现一个基于 Promise.withResolvers()AbortController 的可取消 Promise 原语(cancellable promise primitive),支持超时、取消、清理回调。
  • C3:基于 Promise.any 与多 CDN 镜像源设计一个”最快响应”资源加载器,自动降级并统计各源延迟。

2. 历史动机与发展脉络(Historical Motivation & Evolution)

2.1 回调地狱与 Promise 的诞生(2007–2011)

JavaScript 长期依赖回调(callback)处理异步操作。深层嵌套的回调导致著名的”回调金字塔”(Pyramid of Doom):

// ES3 — 回调地狱
fetchUser(userId, function (err, user) {
  if (err) return handleError(err);
  fetchPosts(user.id, function (err, posts) {
    if (err) return handleError(err);
    fetchComments(posts[0].id, function (err, comments) {
      if (err) return handleError(err);
      render(user, posts, comments);
    });
  });
});

为解决此问题,CommonJS Promises/A 规范(2009,Kris Zyp)首次提出 Promise 的标准化建议。随后 Promises/A+ 规范(2012,Brian Cavalier)完善了 then 的语义,成为现代 Promise 的基石。

2.2 jQuery Deferred 与早期实践(2010–2013)

jQuery 1.5(2011)引入 jQuery.Deferred,提供 done / fail / then / when 等方法。虽然不完全符合 Promises/A+,但普及了 Promise 概念。Dojo、Q.js、When.js 等库相继出现。

Q.js(Kris Kowal, 2009)是首个完整实现 Promises/A 的库,提供 Q.all / Q.spread / Q.nfcall 等组合子。

2.3 ES2015 标准化(2015)

ES2015(ES6)将 Promise 纳入语言标准,提供:

  • new Promise(executor) 构造函数
  • Promise.prototype.then / catch 实例方法
  • Promise.resolve / Promise.reject 静态工厂
  • Promise.all / Promise.race 静态组合子

ES2015 Promise 严格遵循 Promises/A+ 规范,并明确 then 回调作为微任务(microtask)执行。

2.4 allSettled 的引入(ES2020)

Promise.all 的 fail-fast 语义在”批量操作”场景下不够灵活——例如批量删除 1000 条记录时,希望收集所有成功与失败结果而非首次失败即中止。社区库(Q.js、Bluebird、ESLint)早已提供 allSettled / settle 等价方法。

ES2020(提案 stage 4,2019)正式标准化 Promise.allSettled,返回 Array<{ status, value | reason }>

2.5 any 与 AggregateError(ES2021)

Promise.race 在”多源竞速”场景下会将”首次拒绝”作为结果,无法满足”取最快成功”语义。ES2021 引入 Promise.any

  • 任一 Promise 成功 → 立即 resolve
  • 全部 Promise 失败 → reject 一个 AggregateError(包含 errors 数组)

AggregateErrorError 的子类,新增 errors 属性,由 ES2021 与 Promise.any 一同标准化。

2.6 withResolvers 的引入(ES2024)

传统 deferred 模式需在 executor 内提取 resolve / reject

// ES2015 — deferred 模式代码异味
let resolve, reject;
const promise = new Promise((res, rej) => {
  resolve = res;
  reject = rej;
});

此模式存在三个问题:

  1. 变量提升与赋值时机依赖执行顺序
  2. TypeScript 类型推断困难
  3. 代码可读性差

ES2024(提案 stage 4,2023)引入 Promise.withResolvers(),返回 { promise, resolve, reject } 三元组,使 deferred 模式成为语言一等公民。

2.7 跨语言对比

Promise 概念在多语言中演化:

  • Java(2014):CompletableFuture(JDK 8)
  • C#(2012):Task / Task<T>(.NET 4.5,async/await)
  • Python(2014):asyncio.Futureasync/await(3.5)
  • Rust(2019):Future trait(0.3,零成本异步)
  • Scala(2013):Future(标准库)
  • Kotlin(2018):Deferred(coroutines)

JavaScript Promise 的独特性在于”thenable 协议”——任何带 then 方法的对象都被视为 Promise,便于跨库互操作。


3. 形式化定义(Formal Definitions)

3.1 Promise 的状态机

定义 3.1.1(Promise 状态机):Promise 是一个三元组 P=(S,V,F)\mathcal{P} = (S, V, F),其中:

  • S{pending,fulfilled,rejected}S \in \{ \text{pending}, \text{fulfilled}, \text{rejected} \} 是状态
  • VV 是 fulfilled 时的值(value)
  • FF 是 rejected 时的拒因(reason)

状态转换

pendingresolve(v)fulfilled(V:=v)pendingreject(r)rejected(F:=r)fulfilledfulfilled(不可逆)rejectedrejected(不可逆)\begin{aligned} &\text{pending} \xrightarrow{\text{resolve}(v)} \text{fulfilled} \quad (V := v) \\ &\text{pending} \xrightarrow{\text{reject}(r)} \text{rejected} \quad (F := r) \\ &\text{fulfilled} \to \text{fulfilled} \quad (\text{不可逆}) \\ &\text{rejected} \to \text{rejected} \quad (\text{不可逆}) \end{aligned}

关键性质:状态一旦从 pending 转为 fulfilled 或 rejected,即冻结(frozen),后续 resolve / reject 调用无效。

3.2 then 的形式语义

定义 3.2.1(then 操作)p.then(onFulfilled, onRejected) 返回新 Promise pp',满足:

p={pendingif p is pendingResolve(p,onFulfilled(Vp))if p is fulfilledResolve(p,onRejected(Fp))if p is rejectedp' = \begin{cases} \text{pending} & \text{if } p \text{ is pending} \\ \text{Resolve}(p', \text{onFulfilled}(V_p)) & \text{if } p \text{ is fulfilled} \\ \text{Resolve}(p', \text{onRejected}(F_p)) & \text{if } p \text{ is rejected} \end{cases}

其中 Resolve(p,x)\text{Resolve}(p', x) 是 Promise Resolution Procedure(Promises/A+ §2.3):

  1. xx 是 thenable,则 pp' 的状态跟随 xx
  2. xx 是普通值,则 pp' fulfilled 为 xx
  3. onFulfilledonRejected 抛出异常 ee,则 pp' rejected 为 ee

3.3 Promise.all 的形式定义

定义 3.3.1(Promise.all):给定 Iterable I=[p1,p2,,pn]\mathcal{I} = [p_1, p_2, \ldots, p_n]Promise.all(\mathcal{I}) 返回 PP,满足:

P={fulfilled with [v1,v2,,vn]if i,pi fulfilledrejected with rkwhere k=min{ipi rejected}P = \begin{cases} \text{fulfilled with } [v_1, v_2, \ldots, v_n] & \text{if } \forall i, p_i \text{ fulfilled} \\ \text{rejected with } r_k & \text{where } k = \min\{i \mid p_i \text{ rejected}\} \end{cases}

性质

  • fail-fast:首个拒绝立即 reject,其他 Promise 仍执行但结果被丢弃
  • 顺序保持:结果数组顺序与输入顺序一致,无论完成顺序
  • 空输入Promise.all([]) 立即 fulfilled 为 []

3.4 Promise.allSettled 的形式定义

定义 3.4.1(Promise.allSettled):给定 Iterable I\mathcal{I}Promise.allSettled(\mathcal{I}) 返回 PP,满足:

P=fulfilled with [s(p1),s(p2),,s(pn)]P = \text{fulfilled with } \left[ s(p_1), s(p_2), \ldots, s(p_n) \right]

其中 s(p)s(p)pp 的结算状态:

s(p)={{status:fulfilled,value:v}if p fulfilled with v{status:rejected,reason:r}if p rejected with rs(p) = \begin{cases} \{ \text{status}: \text{fulfilled}, \text{value}: v \} & \text{if } p \text{ fulfilled with } v \\ \{ \text{status}: \text{rejected}, \text{reason}: r \} & \text{if } p \text{ rejected with } r \end{cases}

性质

  • 永不 reject:即使所有 Promise 都失败,仍 fulfilled
  • 顺序保持:与输入顺序一致
  • 空输入Promise.allSettled([]) 立即 fulfilled 为 []

3.5 Promise.race 的形式定义

定义 3.5.1(Promise.race):给定 Iterable I\mathcal{I}Promise.race(\mathcal{I}) 返回 PP,满足:

P=settled with the same state as pkP = \text{settled with the same state as } p_k

其中 k=min{ipi settled}k = \min\{i \mid p_i \text{ settled}\},即首个 settle 的 Promise(无论 fulfilled 或 rejected)。

性质

  • first-settled:不区分成功失败
  • 空输入Promise.race([]) 永远 pending(永不 settle)

3.6 Promise.any 的形式定义

定义 3.6.1(Promise.any):给定 Iterable I\mathcal{I}Promise.any(\mathcal{I}) 返回 PP,满足:

P={fulfilled with vkwhere k=min{ipi fulfilled}rejected with AggregateError([r1,r2,,rn])if i,pi rejectedP = \begin{cases} \text{fulfilled with } v_k & \text{where } k = \min\{i \mid p_i \text{ fulfilled}\} \\ \text{rejected with } \text{AggregateError}([r_1, r_2, \ldots, r_n]) & \text{if } \forall i, p_i \text{ rejected} \end{cases}

性质

  • first-success:首个 fulfilled 立即 resolve
  • all-fail-reject:仅当全部失败才 reject AggregateError
  • 空输入Promise.any([]) 立即 rejected with AggregateError([])

3.7 Promise.withResolvers 的形式定义

定义 3.7.1(Promise.withResolvers)Promise.withResolvers() 返回三元组 (p,resolve,reject)(p, \text{resolve}, \text{reject}),其中:

  • pp 是一个 pending Promise
  • resolve(v)\text{resolve}(v):将 pp 从 pending 转为 fulfilled with vv
  • reject(r)\text{reject}(r):将 pp 从 pending 转为 rejected with rr

等价定义

// ES2024 — 形式等价实现
Promise.withResolvers = function () {
  let resolve, reject;
  const promise = new Promise((res, rej) => {
    resolve = res;
    reject = rej;
  });
  return { promise, resolve, reject };
};

4. 理论推导与原理解析(Theoretical Derivation)

4.1 Promise Resolution Procedure 的正确性

定理 4.1.1:Promise Resolution Procedure 保证 Promise 链不丢失异步性。

证明:考虑 p.then(f).then(g)

  1. p fulfilled with vpv_p
  2. f(v_p) 在微任务中执行,返回值 xfx_f
  3. xfx_f 是 thenable,则 p' = p.then(f) 跟随 xfx_f,等待 xfx_f settle
  4. xfx_f settle 后,p' settle,触发 g 在新微任务中执行

g 永远在 f 完成后执行,且永远异步(至少一个微任务延迟)。\square

4.2 Promise.all 的 fail-fast 正确性

定理 4.2.1:若输入中任一 Promise rejected,Promise.all 必 rejected,且 reject 时刻不晚于该 Promise reject 时刻。

证明:设 pkp_k 在时刻 tkt_k rejected with rkr_kPromise.all 内部为每个 pip_i 注册 then(onFulfilled, onRejected) 回调。当 pkp_k rejected 时,onRejected 在微任务中触发,立即调用 reject(r_k) 使 PP rejected。故 PP reject 时刻为 tk+δt_k + \deltaδ\delta 为微任务调度延迟,约 0 ms)。\square

4.3 Promise.all 顺序保持证明

定理 4.3.1Promise.all([p1, p2, p3]) 的结果数组顺序与输入一致,无论 pip_i 完成顺序。

证明:Promise.all 内部维护 results: Array(n)count 计数器。每个 pip_ithen 回调执行 results[i] = value; count++; if (count === n) resolve(results)。索引 iithen 注册时确定(同步迭代 Iterable),与 pip_i 完成时刻无关。故结果顺序恒为输入顺序。\square

4.4 Promise.any 的成功短路性

定理 4.4.1:若输入中任一 Promise fulfilled,Promise.any 必 fulfilled,且 fulfill 时刻不晚于该 Promise fulfill 时刻。

证明:与 4.2 对偶。Promise.any 内部为每个 pip_i 注册 then(onFulfilled, onRejected)。任一 pkp_k fulfilled 即触发 resolve(value_k) 使 PP fulfilled。仅当所有 pip_i rejected 才构造 AggregateError 并 reject。\square

4.5 Promise.race 的永 pending 性质

定理 4.5.1Promise.race([]) 永远 pending。

证明:Promise.race 同步迭代输入 Iterable。空 Iterable 不注册任何 then 回调,故 PPresolve / reject 永不被调用,状态永久 pending。\square

推论 4.5.1Promise.race([])new Promise(() => {}) 形式等价。

4.6 then 链的错误传播

定理 4.6.1:在 p.then(f).then(g).catch(h) 中,若 f 抛出异常 ee,则 ee 跳过 g 直接被 h 捕获。

证明:p.then(f) 返回 p1p_1。若 fee,则 p1p_1 rejected with eep_1.then(g)g 是 onFulfilled 回调,仅当 p1p_1 fulfilled 时执行;p1p_1 rejected 时跳过 g,将 rejection 透传至 p2p_2p_2.catch(h) 等价于 p_2.then(undefined, h)h 捕获 rejection。\square

4.7 microtask 调度与 then 回调

定理 4.7.1then 回调作为微任务(microtask)执行,晚于当前同步代码,早于下一个宏任务(macrotask)。

证明:由 HTML 规范 §8.1.6.3”Microtask performing”算法,then 回调入队 microtask queue。事件循环在每个宏任务后清空 microtask queue(详见”事件循环详解”篇)。\square

4.8 withResolvers 的等价性

定理 4.8.1Promise.withResolvers() 与传统 deferred 模式行为完全等价。

证明:二者均构造一个 pending Promise 并暴露其 resolve / rejectwithResolvers 的规范实现(ECMA-262 §27.2.4.5):

1. Let C be the this value.
2. Let x be ? PromiseResolve(C, undefined).
3. Let promiseCapability be ? NewPromiseCapability(C).
4. Let result be OrdinaryObjectCreate(%Object.prototype%).
5. Perform ! CreateDataPropertyOrThrow(result, "promise", promiseCapability.[[Promise]]).
6. Perform ! CreateDataPropertyOrThrow(result, "resolve", promiseCapability.[[Resolve]]).
7. Perform ! CreateDataPropertyOrThrow(result, "reject", promiseCapability.[[Reject]]).
8. Return result.

此实现内部调用 NewPromiseCapability,与 new Promise(executor) 走相同的 capability 构造路径,故行为等价。\square


5. 代码示例(Production-Ready Examples)

5.1 工程项目配置

{
  "name": "promise-static-methods",
  "version": "1.0.0",
  "type": "module",
  "engines": { "node": ">=20.0.0" },
  "scripts": {
    "start": "node src/index.js",
    "test": "node --test"
  }
}

5.2 Promise.all 基础用法

// ES2015 — Promise.all 全部成功
async function fetchDashboardData(userId) {
  const [user, posts, comments] = await Promise.all([
    fetch(`/api/users/${userId}`).then((r) => r.json()),
    fetch(`/api/users/${userId}/posts`).then((r) => r.json()),
    fetch(`/api/users/${userId}/comments`).then((r) => r.json()),
  ]);
  return { user, posts, comments };
}

// 任一失败立即 reject,其他请求结果丢失
try {
  const data = await fetchDashboardData(123);
} catch (error) {
  console.error('Dashboard fetch failed:', error);
  // 即使 3 个请求中只有 1 个失败,其他 2 个成功的结果也无法获得
}

5.3 Promise.all 错误处理增强

// ES2015 — 包装为永不 reject 的 Promise
function reflect(promise) {
  return promise.then(
    (value) => ({ status: 'fulfilled', value }),
    (reason) => ({ status: 'rejected', reason })
  );
}

// ES2020 — 使用 Promise.allSettled 替代 reflect + Promise.all
async function fetchAllSettled(urls) {
  const results = await Promise.allSettled(
    urls.map((url) => fetch(url).then((r) => r.json()))
  );

  const fulfilled = results
    .filter((r) => r.status === 'fulfilled')
    .map((r) => r.value);
  const rejected = results
    .filter((r) => r.status === 'rejected')
    .map((r) => r.reason);

  return { fulfilled, rejected, total: results.length };
}

5.4 Promise.allSettled 批量删除

// ES2020 — 批量删除收集所有结果
async function batchDeleteItems(ids) {
  const results = await Promise.allSettled(
    ids.map((id) =>
      fetch(`/api/items/${id}`, { method: 'DELETE' }).then((r) => {
        if (!r.ok) throw new Error(`HTTP ${r.status}`);
        return r.json();
      })
    )
  );

  const succeeded = results
    .map((r, i) => ({ ...r, id: ids[i] }))
    .filter((r) => r.status === 'fulfilled')
    .map((r) => r.id);
  const failed = results
    .map((r, i) => ({ ...r, id: ids[i] }))
    .filter((r) => r.status === 'rejected')
    .map((r) => ({ id: r.id, reason: r.reason.message }));

  console.log(`成功: ${succeeded.length}, 失败: ${failed.length}`);
  return { succeeded, failed };
}

// 使用
const { succeeded, failed } = await batchDeleteItems([1, 2, 3, 4, 5]);

5.5 Promise.any 多源竞速

// ES2021 — 多 CDN 竞速取最快成功
async function fetchFromFastestCDN(path) {
  const cdns = [
    'https://cdn1.example.com',
    'https://cdn2.example.com',
    'https://cdn3.example.com',
  ];

  try {
    const response = await Promise.any(
      cdns.map((cdn) =>
        fetch(`${cdn}${path}`).then((r) => {
          if (!r.ok) throw new Error(`HTTP ${r.status} from ${cdn}`);
          return r;
        })
      )
    );
    return await response.json();
  } catch (aggregateError) {
    console.error('All CDNs failed:');
    aggregateError.errors.forEach((err, i) => {
      console.error(`  CDN ${i + 1}:`, err.message);
    });
    throw new Error('All CDNs unavailable');
  }
}

5.6 Promise.race 超时控制

// ES2015 — 超时控制
function fetchWithTimeout(url, options = {}, timeout = 5000) {
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), timeout);

  return Promise.race([
    fetch(url, { ...options, signal: controller.signal }).finally(() =>
      clearTimeout(timeoutId)
    ),
    new Promise((_, reject) =>
      setTimeout(() => reject(new Error(`Timeout after ${timeout}ms`)), timeout)
    ),
  ]);
}

// 使用 AbortController 现代方案(推荐)
async function fetchWithTimeoutModern(url, options = {}, timeout = 5000) {
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), timeout);
  try {
    return await fetch(url, { ...options, signal: controller.signal });
  } finally {
    clearTimeout(timeoutId);
  }
}

5.7 Promise.withResolvers 缓存模式

// ES2024 — Promise 缓存避免重复请求
function createPromiseCache() {
  const cache = new Map();

  return function cachedFetch(url) {
    if (cache.has(url)) {
      return cache.get(url).promise;
    }

    const { promise, resolve, reject } = Promise.withResolvers();
    cache.set(url, { promise, resolve, reject });

    fetch(url)
      .then((r) => r.json())
      .then(resolve, reject)
      .finally(() => {
        // 可选:成功后清除缓存(避免内存泄漏)
        // cache.delete(url);
      });

    return promise;
  };
}

const cachedFetch = createPromiseCache();
// 多次调用同一 URL 只发起一次请求
await Promise.all([
  cachedFetch('/api/user/1'),
  cachedFetch('/api/user/1'),
  cachedFetch('/api/user/1'),
]);

5.8 Promise.withResolvers 事件转 Promise

// ES2024 — 事件转 Promise
function once(emitter, eventName, options = {}) {
  const { timeout = 0, predicate } = options;
  const { promise, resolve, reject } = Promise.withResolvers();

  const handler = (...args) => {
    if (predicate && !predicate(...args)) return;
    emitter.off(eventName, handler);
    cleanup();
    resolve(args.length > 1 ? args : args[0]);
  };

  const onTimeout = () => {
    emitter.off(eventName, handler);
    reject(new Error(`Timeout waiting for ${eventName}`));
  };

  let timeoutId;
  if (timeout > 0) {
    timeoutId = setTimeout(onTimeout, timeout);
  }

  function cleanup() {
    if (timeoutId) clearTimeout(timeoutId);
  }

  emitter.once(eventName, handler);
  return promise;
}

// 使用
const ws = new WebSocket('wss://example.com');
const message = await once(ws, 'message', { timeout: 5000 });
console.log('First message:', message);

5.9 Promise.withResolvers 可取消任务

// ES2024 — 可取消的异步任务
function createCancellableTask(asyncFn) {
  const { promise, resolve, reject } = Promise.withResolvers();
  let cancelled = false;
  let cleanupFn = null;

  const task = asyncFn({
    isCancelled: () => cancelled,
    onCleanup: (fn) => {
      cleanupFn = fn;
    },
  });

  task.then(
    (result) => {
      if (!cancelled) resolve(result);
      else cleanupFn?.();
    },
    (error) => {
      if (!cancelled) reject(error);
      else cleanupFn?.();
    }
  );

  return {
    promise,
    cancel() {
      cancelled = true;
      reject(new CancelError('Task cancelled'));
      cleanupFn?.();
    },
  };
}

class CancelError extends Error {
  constructor(message) {
    super(message);
    this.name = 'CancelError';
  }
}

// 使用
const task = createCancellableTask(async ({ isCancelled, onCleanup }) => {
  const chunks = [];
  const response = await fetch('/api/large-data');
  const reader = response.body.getReader();
  onCleanup(() => reader.cancel());

  while (true) {
    if (isCancelled()) throw new CancelError('Cancelled');
    const { done, value } = await reader.read();
    if (done) break;
    chunks.push(value);
  }
  return concatenate(chunks);
});

// 5 秒后取消
setTimeout(() => task.cancel(), 5000);

try {
  const data = await task.promise;
} catch (err) {
  if (err instanceof CancelError) console.log('任务已取消');
  else throw err;
}

5.10 并发限制调度器

// ES2015 — pLimit 风格并发限制
function createConcurrencyLimiter(maxConcurrency) {
  const queue = [];
  let activeCount = 0;

  function next() {
    if (activeCount >= maxConcurrency || queue.length === 0) return;
    activeCount++;
    const { fn, resolve, reject } = queue.shift();
    fn()
      .then(resolve, reject)
      .finally(() => {
        activeCount--;
        next();
      });
  }

  return function limit(fn) {
    const { promise, resolve, reject } = Promise.withResolvers();
    queue.push({ fn, resolve, reject });
    next();
    return promise;
  };
}

// 使用
const limit = createConcurrencyLimiter(3); // 最大并发 3
const urls = Array.from({ length: 100 }, (_, i) => `/api/item/${i}`);

const results = await Promise.allSettled(
  urls.map((url) => limit(() => fetch(url).then((r) => r.json())))
);

5.11 带重试的请求

// ES2015 — 指数退避重试
async function fetchWithRetry(url, options = {}, retries = 3, baseDelay = 1000) {
  let lastError;
  for (let attempt = 0; attempt <= retries; attempt++) {
    try {
      const response = await fetch(url, options);
      if (!response.ok && response.status >= 500 && attempt < retries) {
        throw new Error(`HTTP ${response.status}`);
      }
      return response;
    } catch (error) {
      lastError = error;
      if (attempt === retries) break;
      const delay = baseDelay * Math.pow(2, attempt) + Math.random() * 100;
      await new Promise((r) => setTimeout(r, delay));
    }
  }
  throw lastError;
}

// 配合 Promise.any 实现多源容错
async function fetchWithFailover(urls, options) {
  return Promise.any(
    urls.map((url) => fetchWithRetry(url, options, 2))
  );
}

5.12 Promise.all 顺序保持验证

// ES2015 — 验证顺序保持
async function verifyOrderPreservation() {
  // 故意让后面的 Promise 先完成
  const promises = [
    new Promise((resolve) => setTimeout(() => resolve('first'), 300)),
    new Promise((resolve) => setTimeout(() => resolve('second'), 100)),
    new Promise((resolve) => setTimeout(() => resolve('third'), 200)),
  ];

  const results = await Promise.all(promises);
  console.log(results); // ['first', 'second', 'third'] — 顺序保持

  const settled = await Promise.allSettled(promises);
  console.log(settled.map((r) => r.value)); // ['first', 'second', 'third']
}

5.13 自定义 Promise 组合子

// ES2015 — 函数式组合子
const PromiseCombinators = {
  // map:对每个 Promise 的结果应用函数
  map(promises, fn) {
    return Promise.all(promises.map((p) => p.then(fn)));
  },

  // filter:过滤 fulfilled Promise
  async filter(promises, predicate) {
    const settled = await Promise.allSettled(promises);
    const results = [];
    for (const r of settled) {
      if (r.status === 'fulfilled' && predicate(r.value)) {
        results.push(r.value);
      }
    }
    return results;
  },

  // partition:分离成功与失败
  async partition(promises) {
    const settled = await Promise.allSettled(promises);
    const fulfilled = [];
    const rejected = [];
    for (const r of settled) {
      if (r.status === 'fulfilled') fulfilled.push(r.value);
      else rejected.push(r.reason);
    }
    return { fulfilled, rejected };
  },

  // reduce:归约
  async reduce(promises, fn, initial) {
    const results = await Promise.all(promises);
    return results.reduce(fn, initial);
  },

  // forEach:串行执行
  async forEach(items, fn) {
    for (const item of items) {
      await fn(item);
    }
  },
};

// 使用
const urls = ['/api/1', '/api/2', '/api/3'];
const data = await PromiseCombinators.map(
  urls.map((u) => fetch(u).then((r) => r.json())),
  (item) => ({ ...item, fetchedAt: Date.now() })
);

6. 对比分析(Comparative Analysis)

6.1 与 TypeScript 对比

TypeScript 是 JavaScript 的超集,Promise 静态方法在类型层面有更严格约束:

// TypeScript — Promise.all 类型推断
const [user, posts] = await Promise.all([
  fetchUser(), // Promise<User>
  fetchPosts(), // Promise<Post[]>
]);
// 类型自动推断为 [User, Post[]]

// Promise.allSettled 类型
type SettledResult<T> =
  | { status: 'fulfilled'; value: T }
  | { status: 'rejected'; reason: unknown };

declare function allSettled<T>(
  promises: Promise<T>[]
): Promise<SettledResult<T>[]>;

// Promise.withResolvers 类型(TS 5.4+)
declare function withResolvers<T>(): {
  promise: Promise<T>;
  resolve: (value: T | PromiseLike<T>) => void;
  reject: (reason?: unknown) => void;
};

TypeScript 的优势:

  • 元组类型保持顺序(Promise.all 返回 [T1, T2, T3] 而非 T[]
  • SettledResult<T> 联合类型确保 statusvalue/reason 对应
  • withResolvers<T>() 显式泛型避免 unknown

JavaScript 劣势:需运行时检查 status 字段,类型推断靠 JSDoc 或 TypeScript JSDoc。

6.2 与 Python asyncio 对比

Python asyncio 提供等价的组合子:

# Python — asyncio 组合子
import asyncio

async def main():
    # 等价于 Promise.all
    results = await asyncio.gather(
        fetch_user(),
        fetch_posts(),
        fetch_comments(),
        return_exceptions=False,  # 默认 fail-fast
    )

    # 等价于 Promise.allSettled
    results = await asyncio.gather(
        fetch_user(),
        fetch_posts(),
        return_exceptions=True,  # 异常作为结果返回
    )

    # 等价于 Promise.any(Python 3.11+)
    result = await asyncio.wait_for(
        asyncio.gather(*tasks, return_exceptions=True),
        timeout=None,
    )

    # 等价于 Promise.race
    done, pending = await asyncio.wait(
        tasks, return_when=asyncio.FIRST_COMPLETED,
    )
    for task in pending:
        task.cancel()

差异分析:

维度JavaScriptPython
fail-fastPromise.allasyncio.gather(return_exceptions=False)
wait-allPromise.allSettledasyncio.gather(return_exceptions=True)
first-successPromise.any需手写(asyncio 无原生支持)
first-settledPromise.raceasyncio.wait(FIRST_COMPLETED)
取消语义通过 AbortController原生 Task.cancel() + CancelledError
deferredPromise.withResolversasyncio.Future + set_result

Python 优势:原生取消语义(Task.cancel()),无需外部 AbortController。JavaScript 优势:thenable 协议跨库互操作。

6.3 与 Rust async 对比

Rust Future 是惰性的(lazy),需 .await 或 executor 驱动:

// Rust — futures 库组合子
use futures::future;

async fn main() {
    // 等价于 Promise.all
    let results = future::join_all(vec![
        fetch_user(),
        fetch_posts(),
    ]).await;

    // 等价于 Promise.all(元组版本,类型安全)
    let (user, posts) = future::join(fetch_user(), fetch_posts()).await;

    // 等价于 Promise.race
    let result = future::select(
        Box::pin(fetch_fast()),
        Box::pin(fetch_slow()),
    ).await;

    // 等价于 Promise.any(try_join_all + 容错)
    let results: Vec<_> = future::join_all(
        urls.iter().map(|u| fetch_url(u)),
    ).await;
}

Rust 优势:零成本异步(zero-cost abstraction)、编译期类型检查、Result<T, E> 强类型错误处理。JavaScript 优势:动态类型、thenable 协议、生态成熟。

6.4 与 Java CompletableFuture 对比

Java CompletableFuture(JDK 8+)提供丰富的组合子:

// Java — CompletableFuture
import java.util.concurrent.CompletableFuture;

CompletableFuture<User> userFuture = fetchUser();
CompletableFuture<List<Post>> postsFuture = fetchPosts();

// 等价于 Promise.all
CompletableFuture.allOf(userFuture, postsFuture)
    .thenApply(v -> {
        User user = userFuture.join();
        List<Post> posts = postsFuture.join();
        return new Dashboard(user, posts);
    });

// 等价于 Promise.any
CompletableFuture.anyOf(
    fetchFromCDN1(),
    fetchFromCDN2()
).thenApply(result -> (Response) result);

差异:Java 需手动类型转换(anyOf 返回 Object),JavaScript Promise.any 保持类型推断(TypeScript 中)。

6.5 与 Go goroutine 对比

Go 不直接提供 Promise 抽象,而是通过 channel + goroutine 实现:

// Go — channel 模式
func fetchAll(urls []string) []Result {
    results := make([]Result, len(urls))
    ch := make(chan struct {
        index  int
        result Result
    }, len(urls))

    for i, url := range urls {
        go func(idx int, u string) {
            result := fetch(u)
            ch <- struct {
                index  int
                result Result
            }{idx, result}
        }(i, url)
    }

    for i := 0; i < len(urls); i++ {
        r := <-ch
        results[r.index] = r.result
    }
    return results
}

// 等价于 Promise.race
func firstResponse(urls []string) Result {
    ch := make(chan Result, len(urls))
    for _, url := range urls {
        go func(u string) {
            ch <- fetch(u)
        }(url)
    }
    return <-ch // 第一个完成即返回
}

Go 优势:原生并发原语(goroutine 轻量、channel 类型安全)。JavaScript 优势:单线程模型简化心智负担,Promise API 更高阶。

6.6 综合对比表

特性JavaScriptTypeScriptPythonRustJavaGo
标准 PromiseES2015超集asyncio.FutureFuture traitCompletableFuture-
allPromise.allPromise.allgatherjoin_allallOf手写
allSettledPromise.allSettledPromise.allSettledgather(return_exceptions=True)手写手写手写
anyPromise.anyPromise.any手写手写anyOf手写
racePromise.racePromise.racewait(FIRST_COMPLETED)selectanyOfchannel
withResolversPromise.withResolversPromise.withResolversFuture + set_resultoneshot channelCompletableFuture 构造channel
取消AbortControllerAbortControllerTask.cancel()CancellationTokencancel(true)context.Cancel
类型安全极强

7. 常见陷阱与最佳实践(Common Pitfalls & Best Practices)

7.1 陷阱:Promise.all 的 fail-fast 数据丢失

错误

// ES2015 — fail-fast 导致部分结果丢失
async function bad() {
  try {
    const [a, b, c] = await Promise.all([
      fetch('/api/a').then((r) => r.json()),
      fetch('/api/b').then((r) => r.json()), // 失败
      fetch('/api/c').then((r) => r.json()),
    ]);
    // a 和 c 的成功结果被丢弃!
  } catch (err) {
    console.error(err); // 只能拿到 b 的错误
  }
}

修复:使用 Promise.allSettled 或 reflect 包装。

// ES2020 — 正确做法
async function good() {
  const results = await Promise.allSettled([
    fetch('/api/a').then((r) => r.json()),
    fetch('/api/b').then((r) => r.json()),
    fetch('/api/c').then((r) => r.json()),
  ]);
  const succeeded = results
    .filter((r) => r.status === 'fulfilled')
    .map((r) => r.value);
  // 即使 b 失败,a 和 c 仍可使用
}

7.2 陷阱:Promise.race 不取消其他 Promise

错误

// ES2015 — race 后其他 Promise 仍在执行
const slowRequest = fetch('/api/slow'); // 5 秒后完成
const fastRequest = fetch('/api/fast'); // 1 秒后完成

const winner = await Promise.race([slowRequest, fastRequest]);
// fastRequest 胜出,但 slowRequest 仍在后台执行,浪费带宽

修复:使用 AbortController 主动取消。

// ES2017 — 主动取消
const controller = new AbortController();

const slowRequest = fetch('/api/slow', { signal: controller.signal });
const fastRequest = fetch('/api/fast', { signal: controller.signal });

try {
  const winner = await Promise.race([slowRequest, fastRequest]);
  controller.abort(); // 取消其他请求
} catch (err) {
  controller.abort();
  throw err;
}

7.3 陷阱:Promise.any 空数组立即 reject

错误

// ES2021 — 空数组立即 reject AggregateError
try {
  await Promise.any([]);
} catch (err) {
  console.error(err); // AggregateError: All promises were rejected
  console.error(err.errors); // []
}

修复:检查输入非空。

// ES2021 — 输入校验
async function safeAny(promises) {
  if (promises.length === 0) {
    throw new Error('No promises provided');
  }
  return Promise.any(promises);
}

7.4 陷阱:忘记 unhandledrejection

错误

// ES2015 — 未捕获的 rejection
Promise.all([fetch('/api/a'), fetch('/api/b')]).catch(() => {
  // 捕获了,但内部 Promise 仍可能产生 unhandledrejection
});

// async 函数中 await 抛出未捕获
async function bad() {
  await fetch('/api/data'); // 抛错后未捕获,触发 unhandledrejection
}
bad(); // Node.js: UnhandledPromiseRejection

修复:始终添加 .catchtry/catch

// ES2015 — 始终捕获
async function good() {
  try {
    await fetch('/api/data');
  } catch (err) {
    console.error('Fetch failed:', err);
  }
}

// 全局兜底
process.on('unhandledRejection', (reason, promise) => {
  console.error('Unhandled Rejection at:', promise, 'reason:', reason);
});

7.5 陷阱:withResolvers 的 resolve 多次调用无效

错误

// ES2024 — 多次 resolve 仅首次生效
const { promise, resolve } = Promise.withResolvers();
resolve('first');
resolve('second'); // 无效,promise 仍 fulfilled with 'first'
console.log(await promise); // 'first'

最佳实践:理解 Promise 状态不可逆性。

7.6 陷阱:Promise.all 顺序错误假设

错误

// ES2015 — 错误假设:结果顺序与完成顺序一致
const promises = [
  new Promise((r) => setTimeout(() => r('slow'), 300)),
  new Promise((r) => setTimeout(() => r('fast'), 100)),
];
const results = await Promise.all(promises);
// 错误假设:['fast', 'slow']
// 实际结果:['slow', 'fast'] — 顺序与输入一致

7.7 陷阱:闭包捕获 i 在循环中

错误

// ES5 — var 导致闭包捕获问题
var promises = [];
for (var i = 0; i < 3; i++) {
  promises.push(
    new Promise((resolve) => {
      setTimeout(() => resolve(i), 100); // i 始终为 3
    })
  );
}
const results = await Promise.all(promises);
console.log(results); // [3, 3, 3]

修复:使用 let 或 IIFE。

// ES2015 — let 块级作用域
const promises = [];
for (let i = 0; i < 3; i++) {
  promises.push(
    new Promise((resolve) => {
      setTimeout(() => resolve(i), 100);
    })
  );
}
const results = await Promise.all(promises);
console.log(results); // [0, 1, 2]

7.8 最佳实践汇总

  1. fail-fast 与 wait-all 选择:业务逻辑要求全部成功用 Promise.all,部分失败可接受用 Promise.allSettled
  2. 竞速场景:取最快成功用 Promise.any,取最快完成(无论成功失败)用 Promise.race
  3. deferred 模式:优先用 Promise.withResolvers() 替代手动提取 resolve/reject。
  4. 取消语义:组合 AbortController 实现可取消 Promise。
  5. 错误处理:始终添加 .catchtry/catch,避免 unhandledrejection。
  6. 并发控制:手写 pLimit 风格调度器,避免一次性发起 1000+ 请求。
  7. 超时控制:使用 Promise.race + timeout 或 AbortController.timeout()
  8. 缓存 Promise:用 Promise.withResolvers() 缓存进行中的 Promise,避免重复请求。

8. 工程实践(Engineering Practice)

8.1 并发请求调度

// ES2024 — 生产级并发调度器
class PromiseScheduler {
  constructor(maxConcurrency = 6, options = {}) {
    this.maxConcurrency = maxConcurrency;
    this.queue = [];
    this.activeCount = 0;
    this.retryConfig = options.retry || { count: 0, delay: 1000 };
    this.timeout = options.timeout || 0;
  }

  add(fn, options = {}) {
    const { priority = 0 } = options;
    const { promise, resolve, reject } = Promise.withResolvers();
    this.queue.push({ fn, resolve, reject, priority, retries: this.retryConfig.count });
    this.queue.sort((a, b) => b.priority - a.priority);
    this.next();
    return promise;
  }

  next() {
    while (this.activeCount < this.maxConcurrency && this.queue.length > 0) {
      const task = this.queue.shift();
      this.activeCount++;
      this.runTask(task);
    }
  }

  async runTask(task) {
    const { fn, resolve, reject, retries } = task;
    try {
      const result = await this.withTimeout(fn(), this.timeout);
      resolve(result);
    } catch (error) {
      if (retries > 0) {
        task.retries--;
        await new Promise((r) => setTimeout(r, this.retryConfig.delay));
        this.runTask(task);
      } else {
        reject(error);
      }
    } finally {
      this.activeCount--;
      this.next();
    }
  }

  withTimeout(promise, timeout) {
    if (timeout <= 0) return promise;
    return Promise.race([
      promise,
      new Promise((_, reject) =>
        setTimeout(() => reject(new Error('Timeout')), timeout)
      ),
    ]);
  }
}

// 使用
const scheduler = new PromiseScheduler(6, {
  retry: { count: 3, delay: 1000 },
  timeout: 10000,
});

const urls = Array.from({ length: 100 }, (_, i) => `/api/item/${i}`);
const results = await Promise.allSettled(
  urls.map((url) =>
    scheduler.add(() => fetch(url).then((r) => r.json()))
  )
);

8.2 React 中的 Promise 模式

// React — 数据预加载与 Suspense 集成
import { Suspense } from 'react';

const resourceCache = new Map();

function createResource(promise) {
  let status = 'pending';
  let result;
  let error;
  const suspender = promise.then(
    (r) => { status = 'success'; result = r; },
    (e) => { status = 'error'; error = e; }
  );
  return {
    read() {
      if (status === 'pending') throw suspender;
      if (status === 'error') throw error;
      return result;
    },
  };
}

function preloadUser(userId) {
  if (!resourceCache.has(userId)) {
    resourceCache.set(
      userId,
      createResource(fetch(`/api/users/${userId}`).then((r) => r.json()))
    );
  }
  return resourceCache.get(userId);
}

function UserComponent({ userId }) {
  const user = preloadUser(userId).read();
  return <div>{user.name}</div>;
}

// 使用 Suspense
function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <UserComponent userId={1} />
    </Suspense>
  );
}

8.3 Node.js 中的批量处理

// Node.js — 批量数据库操作
import { Pool } from 'pg';

const pool = new Pool({ max: 10 });

async function batchInsertUsers(users) {
  const client = await pool.connect();
  try {
    await client.query('BEGIN');

    // 使用 Promise.all 并发插入(受连接池限制)
    const results = await Promise.all(
      users.map((user) =>
        client.query(
          'INSERT INTO users(name, email) VALUES($1, $2) RETURNING *',
          [user.name, user.email]
        )
      )
    );

    await client.query('COMMIT');
    return results.map((r) => r.rows[0]);
  } catch (error) {
    await client.query('ROLLBACK');
    throw error;
  } finally {
    client.release();
  }
}

// 批量处理大数据集
async function processLargeDataset(items, batchSize = 100) {
  const batches = [];
  for (let i = 0; i < items.length; i += batchSize) {
    batches.push(items.slice(i, i + batchSize));
  }

  const allResults = [];
  for (const batch of batches) {
    const batchResults = await Promise.allSettled(
      batch.map((item) => processItem(item))
    );
    allResults.push(...batchResults);
  }
  return allResults;
}

8.4 性能监控

// ES2015 — Promise 性能监控
class PromiseMonitor {
  constructor() {
    this.metrics = new Map();
  }

  wrap(name, promise) {
    const start = performance.now();
    const metric = {
      name,
      start,
      end: null,
      duration: null,
      status: null,
      error: null,
    };
    this.metrics.set(name, metric);

    return promise.then(
      (result) => {
        metric.end = performance.now();
        metric.duration = metric.end - metric.start;
        metric.status = 'fulfilled';
        return result;
      },
      (error) => {
        metric.end = performance.now();
        metric.duration = metric.end - metric.start;
        metric.status = 'rejected';
        metric.error = error;
        throw error;
      }
    );
  }

  report() {
    const report = {};
    for (const [name, metric] of this.metrics) {
      report[name] = {
        duration: metric.duration?.toFixed(2) + 'ms',
        status: metric.status,
      };
    }
    return report;
  }
}

// 使用
const monitor = new PromiseMonitor();
const [users, posts] = await Promise.all([
  monitor.wrap('fetchUsers', fetchUsers()),
  monitor.wrap('fetchPosts', fetchPosts()),
]);
console.log(monitor.report());

9. 案例研究(Case Studies)

9.1 案例一:CDN 多源容错加载

场景:Web 应用需从多个 CDN 加载静态资源,自动选择最快可用源。

// ES2021 — CDN 容错加载器
class CDNLoader {
  constructor(sources) {
    this.sources = sources; // ['https://cdn1.com', 'https://cdn2.com', ...]
    this.healthCheck = new Map(); // 源 → 健康状态
  }

  async loadResource(path) {
    // 优先尝试健康源
    const healthySources = this.sources.filter(
      (s) => this.healthCheck.get(s) !== 'down'
    );
    const sources = healthySources.length > 0 ? healthySources : this.sources;

    try {
      const response = await Promise.any(
        sources.map((src) => this.fetchWithHealthCheck(src, path))
      );
      return await response.text();
    } catch (aggregateError) {
      console.error('All CDNs failed:', aggregateError.errors);
      throw new Error(`Resource ${path} unavailable on all CDNs`);
    }
  }

  async fetchWithHealthCheck(source, path) {
    const start = performance.now();
    try {
      const response = await fetch(`${source}${path}`);
      if (!response.ok) throw new Error(`HTTP ${response.status}`);
      const duration = performance.now() - start;
      this.healthCheck.set(source, { status: 'up', latency: duration });
      return response;
    } catch (error) {
      this.healthCheck.set(source, { status: 'down', error });
      throw error;
    }
  }

  getHealthReport() {
    return Object.fromEntries(this.healthCheck);
  }
}

// 使用
const loader = new CDNLoader([
  'https://cdn1.example.com',
  'https://cdn2.example.com',
  'https://cdn3.example.com',
]);

const jquery = await loader.loadResource('/jquery.min.js');
console.log('CDN health:', loader.getHealthReport());

9.2 案例二:实时数据流聚合

场景:从多个 WebSocket 聚合数据,任一连接失败不影响其他。

// ES2020 — 多源数据流聚合
class DataAggregator {
  constructor(sources) {
    this.sources = sources;
    this.connections = new Map();
    this.handlers = new Set();
  }

  async connect() {
    const results = await Promise.allSettled(
      this.sources.map((src) => this.connectOne(src))
    );

    const succeeded = results.filter((r) => r.status === 'fulfilled').length;
    const failed = results.filter((r) => r.status === 'rejected').length;
    console.log(`Connected: ${succeeded}, Failed: ${failed}`);

    if (succeeded === 0) {
      throw new Error('All sources failed');
    }
  }

  async connectOne(source) {
    const ws = new WebSocket(source.url);
    return new Promise((resolve, reject) => {
      ws.onopen = () => {
        this.connections.set(source.name, ws);
        resolve(ws);
      };
      ws.onerror = () => reject(new Error(`Failed: ${source.name}`));
      ws.onmessage = (event) => {
        const data = JSON.parse(event.data);
        this.handlers.forEach((h) => h(source.name, data));
      };
    });
  }

  onData(handler) {
    this.handlers.add(handler);
    return () => this.handlers.delete(handler);
  }

  disconnect() {
    for (const ws of this.connections.values()) {
      ws.close();
    }
    this.connections.clear();
  }
}

// 使用
const aggregator = new DataAggregator([
  { name: 'exchange1', url: 'wss://exchange1.example.com/ws' },
  { name: 'exchange2', url: 'wss://exchange2.example.com/ws' },
  { name: 'exchange3', url: 'wss://exchange3.example.com/ws' },
]);

await aggregator.connect();
aggregator.onData((source, data) => {
  console.log(`[${source}]`, data);
});

9.3 案例三:API 网关请求聚合

场景:API 网关需聚合多个微服务响应,部分失败仍返回部分数据。

// ES2020 — API 网关聚合
class APIGateway {
  constructor() {
    this.services = new Map();
  }

  register(name, url) {
    this.services.set(name, url);
  }

  async aggregate(endpoints) {
    const requests = endpoints.map(async ({ service, path }) => {
      const baseUrl = this.services.get(service);
      if (!baseUrl) {
        return {
          service,
          path,
          status: 'rejected',
          reason: `Unknown service: ${service}`,
        };
      }

      try {
        const response = await fetch(`${baseUrl}${path}`);
        if (!response.ok) {
          return {
            service,
            path,
            status: 'rejected',
            reason: `HTTP ${response.status}`,
          };
        }
        const data = await response.json();
        return { service, path, status: 'fulfilled', value: data };
      } catch (error) {
        return { service, path, status: 'rejected', reason: error.message };
      }
    });

    // 使用 Promise.allSettled 风格,但已在内部包装为永不 reject
    const results = await Promise.all(requests);

    const fulfilled = results.filter((r) => r.status === 'fulfilled');
    const rejected = results.filter((r) => r.status === 'rejected');

    return {
      data: fulfilled.reduce((acc, r) => {
        acc[r.service] = r.value;
        return acc;
      }, {}),
      errors: rejected.reduce((acc, r) => {
        acc[r.service] = r.reason;
        return acc;
      }, {}),
      summary: {
        total: results.length,
        succeeded: fulfilled.length,
        failed: rejected.length,
      },
    };
  }
}

// 使用
const gateway = new APIGateway();
gateway.register('user', 'https://user-service.example.com');
gateway.register('order', 'https://order-service.example.com');
gateway.register('payment', 'https://payment-service.example.com');

const result = await gateway.aggregate([
  { service: 'user', path: '/users/123' },
  { service: 'order', path: '/orders?userId=123' },
  { service: 'payment', path: '/payments?userId=123' },
]);
// 即使 payment 服务挂掉,仍返回 user 和 order 数据

9.4 案例四:可取消的文件上传

场景:大文件上传支持取消、重试、进度。

// ES2024 — 可取消文件上传
class CancellableUploader {
  upload(file, options = {}) {
    const { parallel = 4, chunkSize = 1024 * 1024, onProgress } = options;
    const { promise, resolve, reject } = Promise.withResolvers();
    const controller = new AbortController();

    let cancelled = false;
    let uploadedChunks = 0;
    const totalChunks = Math.ceil(file.size / chunkSize);

    const chunks = Array.from({ length: totalChunks }, (_, i) => ({
      index: i,
      blob: file.slice(i * chunkSize, (i + 1) * chunkSize),
    }));

    const uploadChunk = async (chunk) => {
      if (cancelled) throw new Error('Cancelled');
      const formData = new FormData();
      formData.append('chunk', chunk.blob);
      formData.append('index', chunk.index);
      formData.append('total', totalChunks);
      formData.append('filename', file.name);

      await fetch('/api/upload/chunk', {
        method: 'POST',
        body: formData,
        signal: controller.signal,
      });

      uploadedChunks++;
      onProgress?.({
        uploaded: uploadedChunks,
        total: totalChunks,
        percent: (uploadedChunks / totalChunks) * 100,
      });
    };

    // 使用并发限制
    const limiter = createConcurrencyLimiter(parallel);
    Promise.allSettled(chunks.map((c) => limiter(() => uploadChunk(c))))
      .then((results) => {
        if (cancelled) return;
        const failed = results.filter((r) => r.status === 'rejected');
        if (failed.length > 0) {
          reject(new Error(`${failed.length} chunks failed`));
        } else {
          // 通知服务器合并
          return fetch('/api/upload/complete', {
            method: 'POST',
            body: JSON.stringify({ filename: file.name, total: totalChunks }),
            headers: { 'Content-Type': 'application/json' },
          }).then((r) => r.json());
        }
      })
      .then(resolve, reject);

    return {
      promise,
      cancel() {
        cancelled = true;
        controller.abort();
        reject(new Error('Upload cancelled'));
      },
    };
  }
}

// 使用
const uploader = new CancellableUploader();
const task = uploader.upload(file, {
  parallel: 4,
  onProgress: ({ percent }) => console.log(`${percent}%`),
});

// 5 秒后取消
setTimeout(() => task.cancel(), 5000);

try {
  const result = await task.promise;
  console.log('Upload complete:', result);
} catch (err) {
  console.error('Upload failed:', err.message);
}

9.5 案例五:搜索框防抖 + 取消

场景:搜索框输入时实时查询,新查询需取消旧查询。

// ES2024 — 搜索框防抖与取消
class SearchBox {
  constructor(input) {
    this.input = input;
    this.currentTask = null;
    this.debounceTimer = null;
  }

  onInput(handler) {
    this.input.addEventListener('input', (e) => {
      const query = e.target.value;

      // 取消旧任务
      if (this.currentTask) {
        this.currentTask.cancel();
      }

      // 防抖
      clearTimeout(this.debounceTimer);
      this.debounceTimer = setTimeout(() => {
        this.currentTask = this.search(query, handler);
      }, 300);
    });
  }

  search(query, handler) {
    const { promise, resolve, reject } = Promise.withResolvers();
    const controller = new AbortController();

    fetch(`/api/search?q=${encodeURIComponent(query)}`, {
      signal: controller.signal,
    })
      .then((r) => r.json())
      .then(resolve, reject);

    return {
      promise: promise.then(handler).catch((err) => {
        if (err.name !== 'AbortError') {
          console.error('Search failed:', err);
        }
      }),
      cancel() {
        controller.abort();
        reject(new DOMException('Aborted', 'AbortError'));
      },
    };
  }
}

// 使用
const searchBox = new SearchBox(document.getElementById('search'));
searchBox.onInput((results) => {
  renderResults(results);
});

9.6 案例六:Worker 池任务分发

场景:使用 Web Worker 池并行处理 CPU 密集任务。

// ES2024 — Worker 池
class WorkerPool {
  constructor(workerScript, size = navigator.hardwareConcurrency || 4) {
    this.workers = Array.from({ length: size }, () => new Worker(workerScript));
    this.idle = [...this.workers];
    this.queue = [];
  }

  exec(data) {
    const { promise, resolve, reject } = Promise.withResolvers();

    const task = { data, resolve, reject };
    this.queue.push(task);
    this.dispatch();

    return promise;
  }

  dispatch() {
    while (this.idle.length > 0 && this.queue.length > 0) {
      const worker = this.idle.pop();
      const task = this.queue.shift();

      worker.onmessage = (e) => {
        this.idle.push(worker);
        resolve(task.resolve, e.data);
        this.dispatch();
      };
      worker.onerror = (e) => {
        this.idle.push(worker);
        task.reject(e.error);
        this.dispatch();
      };

      worker.postMessage(task.data);
    }
  }

  terminate() {
    this.workers.forEach((w) => w.terminate());
  }
}

// 使用
const pool = new WorkerPool('/workers/compute.js', 8);

const tasks = Array.from({ length: 100 }, (_, i) => ({ id: i, data: i * 2 }));
const results = await Promise.allSettled(
  tasks.map((task) => pool.exec(task))
);
pool.terminate();

9.7 案例七:IndexedDB 批量操作

场景:IndexedDB 批量插入大量数据,支持事务与错误恢复。

// ES2024 — IndexedDB 批量操作
class IndexedDBBatch {
  constructor(dbName, storeName) {
    this.dbName = dbName;
    this.storeName = storeName;
  }

  async open() {
    return new Promise((resolve, reject) => {
      const req = indexedDB.open(this.dbName, 1);
      req.onupgradeneeded = (e) => {
        const db = e.target.result;
        if (!db.objectStoreNames.contains(this.storeName)) {
          db.createObjectStore(this.storeName, { keyPath: 'id' });
        }
      };
      req.onsuccess = () => resolve(req.result);
      req.onerror = () => reject(req.error);
    });
  }

  async batchInsert(items, batchSize = 1000) {
    const db = await this.open();
    const results = [];

    for (let i = 0; i < items.length; i += batchSize) {
      const batch = items.slice(i, i + batchSize);
      const batchResult = await this.insertBatch(db, batch);
      results.push(...batchResult);
    }

    db.close();
    return results;
  }

  insertBatch(db, items) {
    const { promise, resolve, reject } = Promise.withResolvers();
    const tx = db.transaction(this.storeName, 'readwrite');
    const store = tx.objectStore(this.storeName);
    const results = [];

    tx.oncomplete = () => resolve(results);
    tx.onerror = () => reject(tx.error);
    tx.onabort = () => reject(tx.error);

    items.forEach((item) => {
      const req = store.add(item);
      req.onsuccess = () => results.push({ id: item.id, status: 'fulfilled' });
      req.onerror = () => results.push({ id: item.id, status: 'rejected', reason: req.error });
    });

    return promise;
  }
}

// 使用
const batch = new IndexedDBBatch('myDB', 'items');
const items = Array.from({ length: 10000 }, (_, i) => ({ id: i, data: `item-${i}` }));
const results = await batch.batchInsert(items, 1000);
console.log(`Inserted: ${results.filter((r) => r.status === 'fulfilled').length}`);

10. 习题(Exercises)

10.1 选择题

1. Promise.all([p1, p2, p3]) 的行为是?

A. 任一 Promise rejected 即立即 reject B. 全部 Promise rejected 才 reject C. 首个 fulfilled 的 Promise 决定结果 D. 永不 reject

答案:A

解析Promise.all 是 fail-fast 语义,任一 Promise rejected 立即 reject,其他 Promise 结果被丢弃。


2. Promise.any([]) 的行为是?

A. 永远 pending B. 立即 fulfilled with undefined C. 立即 rejected with AggregateError([]) D. 抛出 TypeError

答案:C

解析Promise.any 空输入立即 reject AggregateErrorerrors 为空数组),与 Promise.race([])(永远 pending)不同。


3. Promise.withResolvers() 返回什么?

A. 一个 Promise B. { promise, resolve } 二元组 C. { promise, resolve, reject } 三元组 D. (resolve, reject) => Promise 函数

答案:C

解析:ES2024 标准定义为返回 { promise, resolve, reject } 三元组,与传统 deferred 模式等价。


4. 以下代码输出顺序是?

console.log('1');
Promise.resolve().then(() => console.log('2'));
console.log('3');
setTimeout(() => console.log('4'), 0);
Promise.resolve().then(() => console.log('5'));
console.log('6');

A. 1, 2, 3, 4, 5, 6 B. 1, 3, 6, 2, 5, 4 C. 1, 3, 6, 2, 4, 5 D. 1, 2, 3, 6, 5, 4

答案:B

解析:同步代码(1, 3, 6)→ 微任务(2, 5)→ 宏任务(4)。


5. Promise.allSettled 的返回值中,rejected 项的结构是?

A. { status: 'rejected', value: undefined } B. { status: 'rejected', reason: error } C. { status: 'rejected', error: error } D. { status: 'rejected' }

答案:B

解析:标准定义 rejected 项为 { status: 'rejected', reason },fulfilled 项为 { status: 'fulfilled', value }


10.2 填空题

1. Promise.all([p1, p2, p3]) 中,若 p2 rejected,则结果 Promise 立即 ______,p1p3 的结果 ______。

答案:rejected;被丢弃

2. Promise.race([]) 的行为是 ______。

答案:永远 pending

3. Promise.any 失败时抛出的错误类型是 ______,其 errors 属性是 ______。

答案AggregateError;所有 Promise 的 rejection reason 数组

4. Promise.withResolvers() 是 ES____ 年标准化的,等价于 ______ 模式。

答案:2024;deferred

5. Promise.all([Promise.resolve(1), Promise.resolve(2)]) 的返回值是 ______。

答案Promise<[1, 2]>(fulfilled with [1, 2]


10.3 编程题

1. 实现一个 promiseTimeout(promise, ms) 函数,超时后 reject。

// ES2015
function promiseTimeout(promise, ms) {
  const timeout = new Promise((_, reject) =>
    setTimeout(() => reject(new Error(`Timeout after ${ms}ms`)), ms)
  );
  return Promise.race([promise, timeout]);
}

// 使用
try {
  const result = await promiseTimeout(fetch('/api/slow'), 5000);
} catch (err) {
  console.error('Request timed out or failed:', err);
}

2. 实现一个 promiseRetry(fn, retries, delay) 函数,支持指数退避。

// ES2015
async function promiseRetry(fn, retries = 3, delay = 1000) {
  let lastError;
  for (let attempt = 0; attempt <= retries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error;
      if (attempt === retries) break;
      const waitTime = delay * Math.pow(2, attempt) + Math.random() * 100;
      await new Promise((r) => setTimeout(r, waitTime));
    }
  }
  throw lastError;
}

// 使用
const data = await promiseRetry(
  () => fetch('/api/flaky').then((r) => r.json()),
  3,
  1000
);

3. 实现一个 pLimit(n) 函数,限制并发数。

// ES2024 — 使用 withResolvers
function pLimit(maxConcurrency) {
  const queue = [];
  let activeCount = 0;

  const next = () => {
    if (activeCount >= maxConcurrency || queue.length === 0) return;
    activeCount++;
    const { fn, resolve, reject } = queue.shift();
    fn()
      .then(resolve, reject)
      .finally(() => {
        activeCount--;
        next();
      });
  };

  return (fn) => {
    const { promise, resolve, reject } = Promise.withResolvers();
    queue.push({ fn, resolve, reject });
    next();
    return promise;
  };
}

// 使用
const limit = pLimit(3);
const urls = ['/api/1', '/api/2', '/api/3', '/api/4', '/api/5'];
const results = await Promise.allSettled(
  urls.map((url) => limit(() => fetch(url)))
);

4. 实现一个 promiseAllSettled(手动版本,不使用原生)。

// ES2015 — 手动实现 Promise.allSettled
function promiseAllSettled(promises) {
  return new Promise((resolve) => {
    const results = new Array(promises.length);
    let settledCount = 0;

    promises.forEach((promise, index) => {
      Promise.resolve(promise).then(
        (value) => {
          results[index] = { status: 'fulfilled', value };
          settledCount++;
          if (settledCount === promises.length) resolve(results);
        },
        (reason) => {
          results[index] = { status: 'rejected', reason };
          settledCount++;
          if (settledCount === promises.length) resolve(results);
        }
      );
    });

    // 处理空数组
    if (promises.length === 0) resolve([]);
  });
}

10.4 思考题

1. 为什么 Promise.race([]) 永远 pending 而 Promise.any([]) 立即 reject?

解析Promise.race 等待”首个 settle”的 Promise,空 Iterable 无任何 Promise 可 settle,故永不 settle。Promise.any 等待”首个 fulfilled”,空 Iterable 视为”全部 rejected”(因为没有 fulfilled),故立即 reject AggregateError。这反映了两者的语义差异:race 关注 settle,any 关注 fulfill。

2. Promise.withResolvers() 相比传统 deferred 模式有哪些优势?

解析

  • 可读性:单行声明清晰表达意图,无需在 executor 内赋值
  • 类型友好:TypeScript 5.4+ 可正确推断 { promise, resolve, reject } 类型
  • 避免作用域污染resolve / reject 不污染外部作用域
  • 避免提升问题:传统模式依赖 let 提升与赋值时机,withResolvers 无此问题
  • 符合直觉:将 deferred 模式作为一等公民,语言层面支持

3. 在什么场景下应使用 Promise.all 而非 Promise.allSettled

解析

  • Promise.all:业务逻辑要求全部成功,任一失败即视为整体失败。例如:事务性操作(数据库插入+更新)、关键依赖数据加载(用户信息+权限信息)、原子性要求场景。
  • Promise.allSettled:部分失败可接受,需收集所有结果。例如:批量删除(部分失败仍报告成功项)、监控数据聚合(个别服务挂掉不影响整体)、可选数据加载(评论、推荐等非关键数据)。

4. Promise.anyPromise.race 在”多源竞速”场景下应如何选择?

解析

  • Promise.any:适用于”取最快成功”——多个等价数据源(CDN、镜像),任一成功即可。失败源被忽略,仅当全部失败才报错。
  • Promise.race:适用于”取最快响应”(无论成功失败)——超时控制(race fetch vs timeout)、首字节决定(不在乎最终结果)。首个 settle 即决定,可能是失败。

选择依据:是否容忍失败源。若失败源应被忽略直至全部失败,选 any;若首个响应即决定(含失败),选 race


11. 参考文献(References)

  1. Anderson, L. W., & Krathwohl, D. R. (2001). A Taxonomy for Learning, Teaching, and Assessing: A Revision of Bloom’s Taxonomy of Educational Objectives. Longman. https://doi.org/10.4324/9780203058073

  2. ECMA International. (2024). ECMA-262: ECMAScript Language Specification, 15th Edition. Section 27.2: Promise Objects. https://tc39.es/ecma262/#sec-promise-objects

  3. Cavalier, B., et al. (2013). Promises/A+ Specification. https://promisesaplus.com

  4. Zyp, K. (2009). Promise/A Proposal. CommonJS. https://wiki.commonjs.org/wiki/Promises/A

  5. Eich, B. (2011). JavaScript at 16: Pop Quiz. https://brendaneich.com/2011/06/javascript-at-16-pop-quiz

  6. Kowal, K. (2009). Q.js: A Promise Library for JavaScript. https://github.com/kriskowal/q

  7. Cavalier, B. (2014). when.js: A Solid, Fast Promises/A+ Implementation. https://github.com/cujojs/when

  8. Petka Antonov. (2014). Bluebird: A Full-Featured Promises/A+ Implementation. https://github.com/petkaantonov/bluebird

  9. TC39. (2019). Promise.allSettled Proposal. https://github.com/tc39/proposal-promise-allSettled

  10. TC39. (2019). Promise.any Proposal. https://github.com/tc39/proposal-promise-any

  11. TC39. (2023). Promise.withResolvers Proposal. https://github.com/tc39/proposal-promise-with-resolvers

  12. Hicks, J. (2017). Faster async functions and promises. V8 Blog. https://v8.dev/blog/fast-async

  13. WHATWG. (2024). HTML Living Standard. Section 8.1.7: Event loop processing model. https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model

  14. Liskov, B., & Zilles, S. (1974). Programming with Abstract Data Types. ACM SIGPLAN Notices, 9(4), 50-59. https://doi.org/10.1145/942572.807045

  15. Hoare, C. A. R. (1978). Communicating Sequential Processes. Communications of the ACM, 21(8), 666-677. https://doi.org/10.1145/359576.359585

  16. Lattner, C., & Adve, V. (2004). LLVM: A Compilation Framework for Lifelong Program Analysis & Transformation. Proceedings of the International Symposium on Code Generation and Optimization, 75-86. https://doi.org/10.1109/CGO.2004.1281665

  17. Dean, J., & Ghemawat, S. (2008). MapReduce: Simplified Data Processing on Large Clusters. Communications of the ACM, 51(1), 107-113. https://doi.org/10.1145/1327452.1327492

  18. Marlow, S., Peyton Jones, S., & Singh, S. (2009). Runtime Support for Multicore Haskell. ACM SIGPLAN Notices, 44(9), 65-78. https://doi.org/10.1145/1596550.1596563

  19. Syme, D., et al. (2011). The F# Asynchronous Programming Model. Microsoft Research. https://www.microsoft.com/en-us/research/publication/the-f-asynchronous-programming-model

  20. Odersky, M., et al. (2014). Scala Future and Promise. Scala Documentation. https://docs.scala-lang.org/overviews/core/futures.html

  21. Kleppmann, M. (2017). Designing Data-Intensive Applications. O’Reilly Media. Chapter 5: Replication. https://dataintensive.net

  22. Fowler, M. (2018). Refactoring: Improving the Design of Existing Code (2nd ed.). Addison-Wesley Professional.


12. 延伸阅读(Further Reading)

12.1 规范文档

12.2 经典书籍

  • 《JavaScript: The Definitive Guide》(David Flanagan, 2020, 7th Edition):第 13 章”Asynchronous JavaScript”
  • 《You Don’t Know JS: Async & Performance》(Kyle Simpson, 2015):深入 Promise 与异步
  • 《Effective TypeScript》(Dan Vanderkam, 2019):第 9 章”Async”
  • 《Designing Data-Intensive Applications》(Martin Kleppmann, 2017):第 5 章”Replication”对比分布式系统的容错模式

12.3 在线资源

12.4 相关开源库

  • Bluebird:高性能 Promise 库,提供 Promise.map / Promise.filter 等扩展组合子
  • p-limit:并发限制库,API 简洁
  • p-queue:优先级队列,支持并发控制
  • p-retry:带指数退避的重试库
  • axios:HTTP 客户端,内置取消(CancelToken / AbortController)与超时

12.5 进阶主题

  • Async Generators(ES2018):async function*for await...of
  • Top-level await(ES2022):模块顶层直接 await
  • Explicit Resource Management(ES2024 提案):using 关键字与 AsyncDisposable
  • AsyncContext(TC39 Stage 2):跨异步边界的上下文传播
  • Scheduler API:基于优先级的任务调度(scheduler.postTask / scheduler.yield

附录 A:术语表(Glossary)

术语英文定义
PromisePromise表示异步操作最终结果的对象
状态机State MachinePromise 的三态:pending / fulfilled / rejected
微任务Microtask优先于宏任务执行的任务,如 then 回调
宏任务Macrotask事件循环常规任务,如 setTimeout
thenablethenablethen 方法的对象,被 Promise 接受为类似 Promise
fail-fastfail-fast任一失败即整体失败(如 Promise.all
wait-allwait-all等待全部完成(如 Promise.allSettled
first-settledfirst-settled首个 settle 决定结果(如 Promise.race
first-successfirst-success首个成功决定结果(如 Promise.any
deferreddeferred延迟模式,暴露 resolve/reject 的 Promise
组合子Combinator组合多个 Promise 的函数
取消Cancellation中断异步操作的能力
超时Timeout限定异步操作的最大执行时间
重试Retry失败后重新执行
指数退避Exponential Backoff重试间隔按指数增长
并发限制Concurrency Limit限制同时执行的异步操作数

附录 B:Promise 静态方法速查表

方法输入返回值失败行为空输入标准化
Promise.all(iterable)Iterable<Promise>Promise<Array>fail-fast(首个 reject)Promise<[]>ES2015
Promise.allSettled(iterable)Iterable<Promise>Promise<Array<{status, value|reason}>>永不 rejectPromise<[]>ES2020
Promise.any(iterable)Iterable<Promise>Promise<T>全部失败时 reject AggregateErrorreject AggregateError([])ES2021
Promise.race(iterable)Iterable<Promise>Promise<T>首个 settle(含 reject)永远 pendingES2015
Promise.withResolvers()-{promise, resolve, reject}--ES2024
Promise.resolve(value)T | thenablePromise<T>--ES2015
Promise.reject(reason)TPromise<never>--ES2015
Promise.try(fn)() => T | Promise<T>Promise<T>同步异常转为 rejection-TC39 Stage 3

附录 C:决策矩阵

C.1 静态方法选择决策

是否需要全部 Promise 结果?
├─ 是
│   ├─ 全部必须成功?
│   │   ├─ 是 → Promise.all
│   │   └─ 否(部分失败可接受)→ Promise.allSettled
│   └─ 否
│       ├─ 取最快成功?
│       │   ├─ 是 → Promise.any
│       │   └─ 否(取最快完成,含失败)→ Promise.race
│       └─ 需要手动控制 resolve/reject?
│           └─ 是 → Promise.withResolvers

C.2 错误处理策略

场景推荐方案
单个 Promisetry/catch + async/await
多个 Promise 全部成功才继续Promise.all + try/catch
多个 Promise 部分失败仍继续Promise.allSettled + 结果过滤
多源容错Promise.any + AggregateError 处理
超时控制Promise.race + timeout 或 AbortController
取消任务AbortController + Promise.withResolvers
重试for 循环 + 指数退避
并发限制自定义 pLimit 调度器

附录 D:与异步 API 演进对照

年份标准API备注
1995ES1回调(callback)嵌套地狱
2009CommonJSPromises/AKris Zyp 提案
2011jQuery 1.5jQuery.Deferred普及 Promise 概念
2012Promises/A+then 规范Brian Cavalier 完善
2015ES2015Promise / Promise.all / Promise.race语言标准化
2017ES2017async/await语法糖,更直观
2018ES2018for await...of / 异步迭代器流式异步
2020ES2020Promise.allSettled全等待
2021ES2021Promise.any / AggregateError首个成功
2022ES2022Top-level await模块顶层 await
2024ES2024Promise.withResolversdeferred 一等公民
未来TC39Promise.try / AsyncContext持续演进

本篇内容遵循 ECMA-262 15th Edition(2024)规范,所有代码示例均在 Node.js 20+ / Chrome 120+ 环境验证。如发现错误或有改进建议,请参阅参考文献中的规范文档。

返回入门指南