Promise — 把「未来的结果」变成一张可传递的票¶
这一章回答三个问题: Promise 到底是个什么东西(对象?函数?语法?); 它的一生有哪几种状态、状态能不能变;以及
.then().then().then()这条链为什么能成立。 读完你会自己造 Promise、消费 Promise,并能解释"忘记 return 会出什么事"——这是 Promise 最高频的坑。
1. 这一章讲什么¶
前两章铺好了地基:事件循环负责调度(第 01 章),回调是最小单元但编排会烂(第 02 章)。这一章引入** Promise**——第一个专为"编排"而生的抽象。结构:
- 心智图景与三态生命周期(第 3 节);
- 怎么造(执行器)与怎么吃(then/catch/finally)(第 4 节);
- 链式调用:唯一支点、return 纪律、错误传播(第 5 节——本章核心)。
第 00 章天气主线的"三张票捆一捆"(Promise.all)在这一章末尾落地一次,第 04 章再展开全部四个组合方法。
2. 顶层全景:披萨票这个比方¶
原书的比方:网上订披萨,下单时不给你披萨,给你一张票;票有三种状态——做着(待定)、送到了(已兑现)、送不了(已拒绝)1。
这个比方精确得难得,因为每一环都对应机制:
下单(创建 Promise)──→ 票:待定(pending)
│ 后厨出餐
├─ 成功 → 票上盖「已兑现」,附带披萨(结果值)
└─ 失败 → 票上盖「已拒绝」,附带原因(错误)
拿票的人(.then)不需要守在店里:票有结果时,自然有人通知你
Promise 是一个普通的对象,这一点比比方更重要:它能存下来、传给别的函数、放进数组——"未来的结果"第一次成了可以搬来搬去的东西,而回调做不到(回调是行为,不是数据)。
3. 核心原理(一):三态一生,落定不返¶
3.1 三个状态¶
原书定义2:
| 状态 | 含义 | 谁让它进入 |
|---|---|---|
| 待定 pending | 操作还没完 | 创建时的初始状态 |
| 已兑现 fulfilled | 操作成功 | 执行器里调用 resolve(结果) |
| 已拒绝 rejected | 操作失败 | 执行器里调用 reject(错误) |
3.2 落定之后,永不改变¶
这是 Promise 全章最硬的一条性质,原书用了 "immutability"(不可变性)这个词:一旦从待定变成已兑现或已拒绝,状态就锁死了3。resolve 之后再 reject,后者无效。
为什么这条性质值钱?因为它让"结果"变成了可信的:任何人拿到这张票,都只会看到一个确定的结果,不用防着它变。 回调时代没有这个保证——同一个回调理论上可以被调两次、可以先调成功又调失败,消费端得自己处处设防。Promise 把这份防心收进了对象内部。
3.3 主线例子:fetchUserData(123) 的一生¶
本章主线例子是原书构造的"取用户"操作(1 秒耗时是模拟值):用户 ID 是 123 就成功,否则报"User not found"。
function fetchUserData(userId) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (userId === 123) {
resolve({ id: 123, name: "John Doe", email: "john@example.com" });
} else {
reject(new Error("User not found"));
}
}, 1000);
});
}
逐件拆开:
new Promise(...)创建票,创建瞬间状态 = 待定;- 传进构造器的那个函数叫执行器(executor),它立即同步执行——构造 Promise 的那一行就会跑 setTimeout 的登记,不是等一秒后才跑;
- 执行器拿到两个函数:
resolve(出餐)与reject(拒单)。一秒后二选一被调用,票落定。
对应的两个消费场景:
// 票兑现时
fetchUserData(123).then(userData => { console.log("User data:", userData); });
// 票被拒时
fetchUserData(456).catch(error => { console.error("Error:", error.message); });
fetchUserData(456) 这条线里,执行器调用了 reject(new Error("User not found")),于是 .catch 收到的就是当初 reject 时递进去的那个错误对象4。
4. 核心原理(二):三个消费方法的分工¶
原书把 .then()、.catch()、.finally() 各自讲透,分工一句话:then 管成功,catch 管失败,finally 管收尾(成败都跑)5。
.then(onFulfilled, onRejected?) ——第一个参数在兑现时调,可选的第二个参数在拒绝时调。多数代码只传第一个、失败统一交给 catch,因为分开写链更清楚。
.catch(onRejected) ——等价于 .then(null, onRejected),是推荐的失败写法。它有一个链式结构上巨大的好处:链上任何一步抛错或拒了,都会顺着链往下传,直到撞上第一个 catch。三步操作只需挂一个 catch:
fetchUserData(123)
.then(userData => { console.log("User data:", userData); return fetchUserPosts(userData.id); })
.then(posts => { console.log("User posts:", posts); return analyzeUserActivity(posts); })
.catch(error => { console.error("An error occurred:", error); });
// fetchUserData、fetchUserPosts、analyzeUserActivity 任何一步失败,都会汇到这里
对比第 02 章错误优先回调的"每层手写 if (error)":这里错误处理从每层一份变成全链一份——这就是 Promise 对回调三笔债的第二笔(错误分支)的偿还方式。
.finally(callback) ——不管兑现还是拒绝都执行,典型用途是收场:isLoading = false、关掉加载动画、断开数据库连接。
let isLoading = true;
fetchData()
.then(data => console.log("Data fetched:", data))
.catch(error => console.error("Failed to fetch data:", error))
.finally(() => { isLoading = false; updateLoadingIndicator(); });
5. 核心原理(三):链式调用——一条规则撑起一切¶
5.1 唯一支点:.then 永远返回新 Promise¶
链式能成立,全靠这一条:.then() 的返回值永远是一个新的 Promise6。于是上一步的输出可以接下一步的输入,像流水线:
fetchUserData(123)
│ .then(userData => { ...; return fetchUserPosts(userData.id); })
│ ↑ 这个 then 返回新票 A;回调 return 的值,就是票 A 将来兑现的值
▼
票 A .then(posts => { ...; return analyzeUserActivity(posts); })
▼
票 B .then(report => console.log(report))
回调里 return 什么,下一个 .then 就收到什么:return 普通值,下一个 then 立即带着该值被调用;return 另一个 Promise(下一张票),下一个 then 会等那张票落定——"return 下一张票"就是串接异步步骤的口令。
5.2 高频坑:忘记 return¶
原书特意警告:忘了 return,下一个 .then 会立刻执行,收到的值是 undefined,异步操作根本没等7:
// 错:没有 return,posts 是 undefined,而且不等 fetchUserPosts
fetchUserData(123).then(userData => {
console.log("User data:", userData);
fetchUserPosts(userData.id); // 票造出来了,却没递出去
}).then(posts => console.log(posts)); // undefined
5.3 高频坑:嵌套 .then(地狱复活)¶
有了 Promise 还会写出地狱,只要把 .then 再嵌回 .then 里8:
// 错:callback hell 的 Promise 版
fetchUserData(123).then(userData => {
fetchUserPosts(userData.id).then(posts => {
analyzeUserActivity(posts).then(report => { ... });
});
});
正确形状是 5.1 的平链:链上每一步只做两件事——处理当前值,return 下一张票。 链之所以平,是因为"顺序"由 return 表达,不再由缩进表达(第 02 章 6.3 节 waterfall 思想的语言内置版)。
5.4 错误传播的两条细则¶
细则一:错误一路下传,跳过中间的 then。第二步拒了,第三步的 then 不会执行,直接滑到 catch9。
细则二:catch 里 return 普通值,链还能继续。原书的例子:取数据失败时,catch 返回一份默认报告,后面的 then 照常用它10:
fetchUserData(123)
.then(userData => { ...; return fetchUserPosts(userData.id); })
.catch(error => {
console.error("Error fetching data:", error);
return { activity: "low", reason: "Error occurred" }; // 链从这里继续
})
.then(activityReport => { console.log(activityReport); }) // 收到默认报告
.catch(error => console.error("unexpected:", error)); // 只兜后半段的新错误
这也定义了 catch 的位置语义:写在链中间的 catch 是"恢复点",写在末尾的 catch 是"终点站"。
5.5 并行一步:Promise.all 回到天气主线¶
链是串行的;要并行,原书在链里塞进 Promise.all(第 04 章专讲)11:
fetchUserData(123)
.then(userData => Promise.all([fetchUserPosts(userData.id), fetchUserComments(userData.id)]))
.then(([posts, comments]) => { ... })
5.6 链上每个 then 的三种结局¶
链式规则(.then 永远返回新 Promise)有一个重要推论:每个 .then 的回调有且只有三种结局,下一个 .then 的行为完全由它决定。这是本章机制部分的总收口, worth 逐种走一遍(都挂在主线例子上)12:
| 回调的结局 | 下一个 .then 收到什么 |
|---|---|
| return 一个普通值 | 立即被调用,收到那个值 |
| return 一张票(Promise) | 等那张票落定,收到票的结果;票拒了,走 catch |
| 抛错(或调用的票被拒未接) | 被跳过,错误直接滑到最近的 catch |
三种结局在主线例子里各占一步:
fetchUserData(123) ← 造票:待定
.then(userData => { ← 结局一:return fetchUserPosts(...)
console.log(userData); ← 处理普通值(上一步兑现的用户对象)
return fetchUserPosts(userData.id); ← 递出下一张票
})
.then(posts => { ← 结局一再来一次
return analyzeUserActivity(posts);
})
.then(report => console.log(report)) ← 结局一:return 值就是 console.log 的返回值
.catch(error => ...) ← 结局二/三都汇到这里
注意最后一个 then:回调里没写 return,它"返回"的是 console.log 的返回值(undefined)——这在链尾无所谓,但如果后面还挂着 then,就会收到 undefined。return 纪律在链的每一环都生效,链尾也不例外。
5.7 错误传播的完整图谱¶
第 03 章正文给了错误传播的两条细则,这里把它们合成一张完整的图。链上任何一点出问题,只有两种流向13:
正常: then ─→ then ─→ then ─→ finally
出错: then ─→ then ─× ↓(跳过中间所有 then)
catch ─→ 恢复(return 值)──→ then 继续 ─→ finally
catch ─→ 再抛出 ──────────→ 外层 catch ─→ finally
无 catch 兜底:错误滑出链尾 → 未处理的拒绝(第 04 章 4 节的兜底对象)
三个容易踩的边角,逐个说:
- catch 也是 then。
.catch(f)等价于 `.then(null, f)——所以 catch 自己也有"三种结局":return 值(恢复)、return 票(继续等)、再抛(继续传)。第 03 章正文 5.4 的例子就是 catch return 值恢复链。 - finally 不改变值。 finally 的回调不接收结果也不该返回替代值;它返回什么都会被忽略,链把 finally 之前的值/错误原样递过去。它的唯一职责是副作用(关连接、停动画)。
- 每个 then 挂得越晚,越不受影响。 错误只往下传,不传回上面——所以链中间的 catch 是"分水岭":它之前的错误它管,它之后的错误它管不着。
5.8 落定的票再挂回调,照样能收到¶
正文例子都是"先挂 .then,后落定"。实际顺序反过来也完全合法:
这之所以能成立,正因为票是个对象:它把"结果"存了下来,任何时刻来的回调都能取走。回调做不到这一点——函数递过去的时候不在,之后就再也收不到那个时刻了。"票能存钱,回调只是动作"——这是 Promise 对回调三笔暗账(第 02 章 6.6)的根本解法。
5.9 造票现场:执行器到底什么时候跑¶
3.3 说"执行器立即同步执行",这个事实值得一个专门的实验,因为它解释了新手最常见的惊讶14:
console.log('① 创建之前');
const p = new Promise((resolve, reject) => {
console.log('② 执行器内部'); // 这行在创建那一行就跑
resolve(123);
});
console.log('③ 创建之后');
p.then(v => console.log('④ then:', v));
console.log('⑤ 注册 then 之后');
输出:① → ② → ③ → ⑤ → ④。两个要点:② 夹在①③之间,证明执行器是同步的——new Promise 不是"预约一个将来才跑的任务",而是"立刻执行一段代码,并把它的两个出口(resolve/reject)交给你";④ 排在⑤后面,证明 .then 注册的回调是异步的——哪怕票在注册前已经兑现,回调也要排进微任务队列,不会插队。把这两点合起来,就是 Promise 的完整时序性格:造票同步,吃票异步。
这个性格解释了 5.8 的"后挂不丢",也解释了另一个常见困惑:为什么 new Promise 里不写 resolve,代码不会报错——执行器跑完就结束了,票永远停在待定状态(第 08 章错误三的根源)。
5.10 then 的两个参数:什么场景还在用¶
正文提过 .then(onFulfilled, onRejected) 可以一次传两个函数,随后又说"多数代码只传第一个"。那一个参数形态什么时候是正确选择15?
fetchUserData(123).then(
userData => console.log(userData), // 成功分支
error => console.error('仅这一步的失败:', error) // 只兜这一个 then 的失败
);
与"then 只传成功 + 链尾 catch"的差别在错误范围:两个参数的形态,错误处理只覆盖这一步——如果回调自己抛了错,第二个函数接不到(它只管上一张票的拒绝)。所以它的适用场景很窄:你想对"上一步失败"做一件与其他步骤不同的事(比如这一步的失败用默认值顶上,其他步骤的失败照常上抛)。链中间用"then 两参"当恢复点,比"catch 当恢复点"更精确——catch 会把同一步里回调抛出的错也吞掉。但多数团队为一致性仍然选 catch——这是风格之争,不是对错之争。
5.11 finally 的 two 个"不改变"¶
finally 的语义有两个"不改变",原书的 isLoading 例子只展示了第一个16:
其一,不改变流程走向。 成功链最后挂 finally,链仍按成功继续;失败链挂 finally,错误照样往下传(finally 自己不接错误,后面得有 catch 才收得住)。finally 是"路过时一定敲一下的门",不是"换道"。
其二,不改变传递的值。 finally 回调拿不到结果(没有参数),它 return 什么也会被忽略——链把 finally 之前的值或错误原样递给下一环。所以 finally 里"顺手 return 一个清理结果"是纯粹的无效代码。
这两个"不改变"把 finally 钉死在"副作用专用位":停加载动画、关数据库连接、记录操作流水。它管"收尾动作",不管"收尾结果"——想要"失败时返回默认值",那是 catch 的活。
5.12 链式 + 并行:链中间的 Promise.all 走查¶
5.5 末尾的链内并行例子值得一次完整的走查,因为它是"串里有并"的最小样例17:
fetchUserData(123)
.then(userData => {
console.log('User data:', userData);
return Promise.all([
fetchUserPosts(userData.id),
fetchUserComments(userData.id),
]);
})
.then(([posts, comments]) => {
console.log('Posts:', posts);
console.log('Comments:', comments);
return analyzeUserActivity(posts, comments);
})
.then(report => console.log(report))
.catch(error => console.error(error));
逐步看内部状态(耗时为演示编):t=0 第一张票造出;t=1 秒用户数据到,第一个 then 执行——Promise.all 在这一刻才把帖子、评论两张票同时发出(注意是这一刻,不是 t=0);t=2 秒两张票都到,all 兑现,按输入顺序交出 [posts, comments],解构(把数组按位置拆开)后进入第二个 then;t=2 秒 analyze 的票发出;t=3 秒报告到。整条链:1 秒串行(必须等用户数据)+ 1 秒并行段 + 1 秒串行。
这个形状在真实代码里占比极高:"先串一步拿到关键 ID,再并行扇出取关联数据,再串一步汇总"——第 06 章的混合编排(3.2 的三段式)就是它去掉链、换成 await 的样子。如果这一步走错(比如在第一个 then 里逐个 await 两张票),并行段就塌成串行,总耗时从 3 秒变 4 秒,而且代码看起来毫无破绽——这正是第 05 章 6.1 要复查的那一处。
5.13 待定状态长什么样:一个控制台实验¶
3.1 的 pending 例子有一个配套观察,原书把它写成了注释:
const pendingPromise = new Promise((resolve, reject) => {
setTimeout(() => { resolve("Operation completed"); }, 2000);
});
console.log(pendingPromise); // Promise {<pending>}
打印一张还没落定的票,控制台显示 Promise {<pending>}——状态字面量就印在对象上。两秒后同一个对象再打印,会变成 Promise {<fulfilled>: "Operation completed"}18。这个实验值得亲手做一次,因为它把"三态"从定义变成了看得见的东西:票是对象,所以它的状态能被随时查看——回调永远给不了你这个观察窗口。 这也是第 08 章调试一节"Console 里看票"的基础。
5.13a 兑现值与拒因:递进去什么,拿出来什么¶
主线例子的另一个细节:resolve({ id: 123, name: "John Doe", email: "john@example.com" }) 递进去一个对象,第一个 then 拿到的 userData 就是同一个对象——resolve/reject 只是"放行",不做拷贝、不做转换。同理 reject 递进去的 new Error(...),会被 catch 原样收到。这个"原样传递"值得记住,因为它决定了你能递什么:任何能存下来的东西(数字、字符串、对象、数组、另一张票)都能递;也因此,票的结果可以是一张票——这是 5.6"三种结局"里 return 票能成立的前提。
5.14 消费端的三种登记姿势¶
同一张票,消费端有三种登记姿势,效果依次增强19:
// 姿势一:只关心成功
fetchUserData(123).then(userData => { ... });
// 姿势二:成功失败都登记(推荐)
fetchUserData(123)
.then(userData => { ... })
.catch(error => { ... });
// 姿势三:再加收尾
fetchUserData(123)
.then(userData => { ... })
.catch(error => { ... })
.finally(() => { isLoading = false; });
姿势一的隐患要单独说:没有 catch 的链,一旦拒绝就是"未处理的拒绝"——浏览器里它静默消失(控制台可能有警告),Node.js 里它是进程级事件(第 04 章 4 纪律五的兜底对象)。从写下第一个 Promise 起,姿势二就该是条件反射。 姿势三留给"成败都要做的一件事"(关动画、清计时器)。
5.15 本章机制一页总复习¶
把第 3-5 节的机制压成一页,考前(面试前)翻这张就够:
造票:new Promise((resolve, reject) => { ... })
├─ 执行器同步执行
├─ resolve(v) → fulfilled(value = v)
└─ reject(e) → rejected(reason = e) 落定后不可逆
吃票:ticket.then(onF, onR) ticket.catch(onR) ticket.finally(cb)
└─ 全部返回【新票】;回调进微任务队列(第 01 章)
链:回调 return 什么,下一个 then 收到什么
├─ return 值 → 下环立即带值执行
├─ return 票 → 下环等票落定
└─ throw / 被拒 → 跳过中间 then,滑到最近 catch
catch 里 return → 链恢复;catch 里再 throw → 继续传
组合:all(全成) / race(先落定) / allSettled(全落定) / any(先成) ← 第 04 章
6. 作者的判断与证据¶
本章没有外部证据,判断全在机制设计里,最值得带走的作者立场有三处:
- 不可变性被当作"关键特性"显式点出(3.2)——作者认为信任比灵活重要3;
- "忘记 return"和"嵌套 then"被单列成 warning——作者预判了读者从回调迁过来时最容易复发的两个习惯78;
- catch 的中途恢复语义被正面教授(return 默认值让链继续),不只是兜底——错误被重新定义为"流程的一种正常走向"10。
7. 边界与局限¶
- 执行器里抛出的异常会自动变成拒绝——这条重要的便利规则本章没展开,要到第 08 章的调试错误里才间接用上;
Promise.all在这里只露一面,"有一个拒了全体拒"的短路语义没讲,第 04 章补;- 没有讲 Promise 落定后"再挂 then 也能收到结果"这个常用事实——5.8 补上了;
- 没有讲
Promise.resolve(v)/Promise.reject(e)这两个快捷造票函数:它们把"一张已兑现/已拒绝的票"一行造出,在测试和默认值场景很常用(第 11 章 Jest 的 mock 里Promise.resolve({...})就是它们); - 没有讲把回调式接口包成 Promise 的标准手法(
new Promise包一层),第 05、07 章的改造例子会碰到它的边角。 - 没有讲
Promise.resolve(v)/Promise.reject(e)两个快捷造票函数(一行造出已兑现/已拒绝的票),它们在测试 mock 与默认值场景很常用,第 11 章会见到Promise.resolve({...})。
8. 可带走的¶
- Promise 是一个普通对象,代表一个未来的结果;三态:待定→已兑现/已拒绝;
- 落定之后永不改变——同一个结果所有人看到都一样,这是它对回调的第一笔偿还;
- 执行器(new Promise 里那个函数)立即同步执行;resolve/reject 决定票的命运;
- 消费三件套:then 管成功、catch 管失败、finally 管收尾;全链一个 catch 接住所有步的错误;
- 链的唯一支点:.then 永远返回新 Promise;回调 return 的值/票,就是下一个 then 的输入;
- 两个高频坑:忘记 return(下一个 then 收到 undefined 且不等待)、把 then 套进 then(地狱复活);
- 链中间的 catch 是恢复点(return 默认值,链继续),链末尾的 catch 是终点站。
- resolve/reject 原样传递:递进去的对象、错误、另一张票,消费端拿到的就是同一个;票甚至可以兑现成另一张票——链式传递的地基。 也因为原样传递,深拷贝不属于 Promise 的职责——要副本自己拷。
9. 原文地图¶
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| 披萨票三态 | Chapter 3: Promises | text/06-ch03-chapter-3-promises.txt:11(搜「pizza」) |
| 执行器结构 | 同上 | text/06-ch03-chapter-3-promises.txt:34(搜「executor」) |
| 三态生命周期 | 同上 | text/06-ch03-chapter-3-promises.txt:42(搜「Pending」) |
| 不可变性 | 同上 | text/06-ch03-chapter-3-promises.txt:94(搜「immutability」) |
| fetchUserData(123) 主线 | 同上 | text/06-ch03-chapter-3-promises.txt:120(搜「fetchUserData」) |
| then/catch/finally 分工 | 同上 | text/06-ch03-chapter-3-promises.txt:139(搜「then()」) · text/06-ch03-chapter-3-promises.txt:167(搜「finally()」) |
| catch 等价于 then(null,…) | 同上 | text/06-ch03-chapter-3-promises.txt:220(搜「equivalent」) |
| finally 的 isLoading 例子 | 同上 | text/06-ch03-chapter-3-promises.txt:264(搜「isLoading」) |
| 嵌套 then 问题 | 同上 | text/06-ch03-chapter-3-promises.txt:310(搜「difficult to read」) |
| 链式与 return 纪律 | 同上 | text/06-ch03-chapter-3-promises.txt:314(搜「chaining」) · text/06-ch03-chapter-3-promises.txt:368(搜「forget」) |
| 错误传播与 catch 恢复 | 同上 | text/06-ch03-chapter-3-promises.txt:372(搜「propagate」) · text/06-ch03-chapter-3-promises.txt:385(搜「default activity report」) |
| 链内 Promise.all | 同上 | text/06-ch03-chapter-3-promises.txt:404(搜「Promise.all」) |
-
出处:「Chapter 3: Promises」第 11-17 段(
text/06-ch03-chapter-3-promises.txt:11,搜「pizza」)。订披萨比方与三态:prepared(pending)、delivered successfully(fulfilled)、can't be delivered(rejected)。 ↩ -
出处:同章第 40-46 段(
text/06-ch03-chapter-3-promises.txt:42,搜「Pending」)。三态定义照原文。 ↩ -
出处:同章第 94 段(
text/06-ch03-chapter-3-promises.txt:94,搜「immutability」)。原文:"once a promise settles… its state cannot change. This immutability is a key feature of promises." ↩↩ -
出处:同章第 120-133 段(
text/06-ch03-chapter-3-promises.txt:127,搜「User not found」)。主线例子的构造与成功/失败两个分支照原书。 ↩ -
出处:同章第 186-278 段(
text/06-ch03-chapter-3-promises.txt:192,搜「onFulfilled」;:220,搜「catch()」;:249,搜「finally()」)。三个方法的定义与 isLoading 例子分别位于这些段。 ↩ -
出处:同章第 205 段(
text/06-ch03-chapter-3-promises.txt:205,搜「returns a new promise」)。原文:"The .then() method always returns a new promise, which allows for chaining." ↩ -
出处:同章第 364-368 段(
text/06-ch03-chapter-3-promises.txt:368,搜「forget」)。原文:"If you forget to return the promise, the next .then() will execute immediately… before the asynchronous operation completes." ↩↩ -
出处:同章第 286-310 段(
text/06-ch03-chapter-3-promises.txt:310,搜「difficult to read」)。嵌套结构示例照原书,"This nested structure quickly becomes difficult to read and maintain"。 ↩↩ -
出处:同章第 372-381 段(
text/06-ch03-chapter-3-promises.txt:372,搜「propagate」)。原文:"errors will propagate down the chain until they encounter a .catch()"。 ↩ -
出处:同章第 383-395 段(
text/06-ch03-chapter-3-promises.txt:385,搜「default activity report」)。catch 返回默认活动报告后链继续到最终 then。 ↩↩ -
出处:同章第 399-421 段(
text/06-ch03-chapter-3-promises.txt:404,搜「Promise.all」)。链内并行取 posts 与 comments 的例子照原书。 ↩ -
出处:「Chapter 3: Promises」第 312-368 段(
text/06-ch03-chapter-3-promises.txt:314,搜「chaining」;:364,搜「Returning Promises」)。链式结构、"ensuring that you return promises"与"forget to return"的警告照原书。 ↩ -
出处:「Chapter 3: Promises」第 370-395 段(
text/06-ch03-chapter-3-promises.txt:372,搜「propagate」;:386,搜「default activity report」)。错误传播与 catch 恢复照原书。 ↩ -
出处:「Chapter 3: Promises」第 50-64 段(
text/06-ch03-chapter-3-promises.txt:54,搜「pendingPromise」)。pending 例子与"remains in the pending state for two seconds"的时序照原书;①-⑤实验的"造票同步、吃票异步"是对执行器与 .then 语义的推导。 ↩ -
出处:「Chapter 3: Promises」第 188-204 段(
text/06-ch03-chapter-3-promises.txt:192,搜「onFulfilled」)。then 两参数的定义照原书;"第二个参数只管上一张票的拒绝"是对其等价形式 .then(null, f) 的展开。 ↩ -
出处:「Chapter 3: Promises」第 247-278 段(
text/06-ch03-chapter-3-promises.txt:264,搜「isLoading」)。finally 的 isLoading 收尾例子照原书。 ↩ -
出处:「Chapter 3: Promises」第 397-421 段(
text/06-ch03-chapter-3-promises.txt:404,搜「Promise.all」)。链内并行的示例与解构写法照原书。 ↩ -
出处:「Chapter 3: Promises」第 50-64 段(
text/06-ch03-chapter-3-promises.txt:62,搜「pending」)。注释// Promise {<pending>}与两秒后 resolve 的时序照原书。 ↩ -
出处:「Chapter 3: Promises」第 135-180 段(
text/06-ch03-chapter-3-promises.txt:145,搜「catch」;:169,搜「finally」)。then/catch/finally 的登记方式照原书。 本章的每一节都对应后面章节的一处引用——读 04-06 章迷路时,回 5.15 的总复习图对坐标。 ↩