在 Ajax 进阶篇(ajax 封装) 之后,用 Promise 统一异步结果:三种状态、
then/catch/finally链式调用、ajaxPromise 与回调地狱扁平化。为 async/await 打底。
目录
- 零、导读与学习价值
- [0.5 本章在学什么(知识地图)](#0.5 本章在学什么(知识地图))
- [0.6 配套可运行示例(一分钟自检)](#0.6 配套可运行示例(一分钟自检))
- [0.1 案例覆盖清单](#0.1 案例覆盖清单)
- [0.2 核心名词速查](#0.2 核心名词速查)
- [0.3 与 Ajax 基础篇 / Ajax 进阶篇 的衔接](#0.3 与 Ajax 基础篇 / Ajax 进阶篇 的衔接)
- [0.4 建议练习路线](#0.4 建议练习路线)
- [1. 异步编程概述](#1. 异步编程概述)
- [1.1 同步与异步](#1.1 同步与异步)
- [1.2 异步编程的场景](#1.2 异步编程的场景)
- [1.3 回调函数的问题](#1.3 回调函数的问题)
- [1.4 配套可运行示例(同步 vs 异步)](#1.4 配套可运行示例(同步 vs 异步))
- [1.5 配套可运行示例(三层嵌套回调)](#1.5 配套可运行示例(三层嵌套回调))
- [2. Promise 基本概念](#2. Promise 基本概念)
- [2.1 Promise 是什么?](#2.1 Promise 是什么?)
- [2.2 Promise 的特点](#2.2 Promise 的特点)
- [2.3 Promise 的基本使用](#2.3 Promise 的基本使用)
- [2.4 配套示例:创建 Promise 与 executor](#2.4 配套示例:创建 Promise 与 executor)
- [2.5 配套可运行示例(状态不可逆)](#2.5 配套可运行示例(状态不可逆))
- [3. Promise 状态详解](#3. Promise 状态详解)
- [3.1 三种状态](#3.1 三种状态)
- [3.2 状态转换示例](#3.2 状态转换示例)
- [3.3 状态检查](#3.3 状态检查)
- [3.4 配套可运行示例(状态不可逆)](#3.4 配套可运行示例(状态不可逆))
- [3.5 配套可运行示例(pending → fulfilled)](#3.5 配套可运行示例(pending → fulfilled))
- [4. Promise 基础语法](#4. Promise 基础语法)
- [4.1 Promise 构造函数](#4.1 Promise 构造函数)
- [4.2 resolve 和 reject](#4.2 resolve 和 reject)
- [4.3 配套可运行示例(then 双回调)](#4.3 配套可运行示例(then 双回调))
- [5. Promise 实例方法](#5. Promise 实例方法)
- [5.1 then() 方法](#5.1 then() 方法)
- [5.1.1 配套可运行示例(基础 then)](#5.1.1 配套可运行示例(基础 then))
- [5.2 then() 返回值规则](#5.2 then() 返回值规则)
- [5.3 catch() 方法](#5.3 catch() 方法)
- [5.4 finally() 方法](#5.4 finally() 方法)
- [5.5 配套可运行示例(then 返回值)](#5.5 配套可运行示例(then 返回值))
- [5.6 配套可运行示例(catch + finally)](#5.6 配套可运行示例(catch + finally))
- [6. Promise 链式调用](#6. Promise 链式调用)
- [6.1 链式调用原理](#6.1 链式调用原理)
- [6.4 配套示例:Ajax 回调地狱 → Promise 链](#6.4 配套示例:Ajax 回调地狱 → Promise 链)
- [6.4.2 配套可运行示例(链尾 catch)](#6.4.2 配套可运行示例(链尾 catch))
- [6.4.1 延伸练习:新歌榜前十首](#6.4.1 延伸练习:新歌榜前十首)
- [6.5 配套示例:Node 顺序读文件](#6.5 配套示例:Node 顺序读文件)
- [6.2 链式调用最佳实践](#6.2 链式调用最佳实践)
- [6.3 错误传递机制](#6.3 错误传递机制)
- [6.6 配套可运行示例(链式 mock)](#6.6 配套可运行示例(链式 mock))
- [7. Promise 常见应用](#7. Promise 常见应用)
- [7.1 网络请求封装](#7.1 网络请求封装)
- [7.2 定时器封装](#7.2 定时器封装)
- [7.3 文件操作封装](#7.3 文件操作封装)
- [7.4 配套可运行示例(定时器 Promise)](#7.4 配套可运行示例(定时器 Promise))
- [7.5 配套可运行示例(ajaxPromise 最小版)](#7.5 配套可运行示例(ajaxPromise 最小版))
- [8. Promise 静态方法](#8. Promise 静态方法)
- [8.1 Promise.resolve()](#8.1 Promise.resolve())
- [8.2 Promise.reject()](#8.2 Promise.reject())
- [8.2.1 配套可运行示例(仅 reject)](#8.2.1 配套可运行示例(仅 reject))
- [8.3 配套可运行示例(Promise.resolve / reject)](#8.3 配套可运行示例(Promise.resolve / reject))
- [9. 调试与错误处理](#9. 调试与错误处理)
- [9.1 错误处理最佳实践](#9.1 错误处理最佳实践)
- [9.2 调试技巧](#9.2 调试技巧)
- [9.3 配套可运行示例(链式调试)](#9.3 配套可运行示例(链式调试))
- 高频面试题速查
- 总结
零、导读与学习价值
0.5 本章在学什么(知识地图)
#mermaid-svg-E9bwpOZqVZgnMS9f{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-E9bwpOZqVZgnMS9f .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-E9bwpOZqVZgnMS9f .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-E9bwpOZqVZgnMS9f .error-icon{fill:#552222;}#mermaid-svg-E9bwpOZqVZgnMS9f .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-E9bwpOZqVZgnMS9f .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-E9bwpOZqVZgnMS9f .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-E9bwpOZqVZgnMS9f .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-E9bwpOZqVZgnMS9f .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-E9bwpOZqVZgnMS9f .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-E9bwpOZqVZgnMS9f .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-E9bwpOZqVZgnMS9f .marker{fill:#333333;stroke:#333333;}#mermaid-svg-E9bwpOZqVZgnMS9f .marker.cross{stroke:#333333;}#mermaid-svg-E9bwpOZqVZgnMS9f svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-E9bwpOZqVZgnMS9f p{margin:0;}#mermaid-svg-E9bwpOZqVZgnMS9f .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-E9bwpOZqVZgnMS9f .cluster-label text{fill:#333;}#mermaid-svg-E9bwpOZqVZgnMS9f .cluster-label span{color:#333;}#mermaid-svg-E9bwpOZqVZgnMS9f .cluster-label span p{background-color:transparent;}#mermaid-svg-E9bwpOZqVZgnMS9f .label text,#mermaid-svg-E9bwpOZqVZgnMS9f span{fill:#333;color:#333;}#mermaid-svg-E9bwpOZqVZgnMS9f .node rect,#mermaid-svg-E9bwpOZqVZgnMS9f .node circle,#mermaid-svg-E9bwpOZqVZgnMS9f .node ellipse,#mermaid-svg-E9bwpOZqVZgnMS9f .node polygon,#mermaid-svg-E9bwpOZqVZgnMS9f .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-E9bwpOZqVZgnMS9f .rough-node .label text,#mermaid-svg-E9bwpOZqVZgnMS9f .node .label text,#mermaid-svg-E9bwpOZqVZgnMS9f .image-shape .label,#mermaid-svg-E9bwpOZqVZgnMS9f .icon-shape .label{text-anchor:middle;}#mermaid-svg-E9bwpOZqVZgnMS9f .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-E9bwpOZqVZgnMS9f .rough-node .label,#mermaid-svg-E9bwpOZqVZgnMS9f .node .label,#mermaid-svg-E9bwpOZqVZgnMS9f .image-shape .label,#mermaid-svg-E9bwpOZqVZgnMS9f .icon-shape .label{text-align:center;}#mermaid-svg-E9bwpOZqVZgnMS9f .node.clickable{cursor:pointer;}#mermaid-svg-E9bwpOZqVZgnMS9f .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-E9bwpOZqVZgnMS9f .arrowheadPath{fill:#333333;}#mermaid-svg-E9bwpOZqVZgnMS9f .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-E9bwpOZqVZgnMS9f .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-E9bwpOZqVZgnMS9f .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-E9bwpOZqVZgnMS9f .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-E9bwpOZqVZgnMS9f .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-E9bwpOZqVZgnMS9f .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-E9bwpOZqVZgnMS9f .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-E9bwpOZqVZgnMS9f .cluster text{fill:#333;}#mermaid-svg-E9bwpOZqVZgnMS9f .cluster span{color:#333;}#mermaid-svg-E9bwpOZqVZgnMS9f div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-E9bwpOZqVZgnMS9f .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-E9bwpOZqVZgnMS9f rect.text{fill:none;stroke-width:0;}#mermaid-svg-E9bwpOZqVZgnMS9f .icon-shape,#mermaid-svg-E9bwpOZqVZgnMS9f .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-E9bwpOZqVZgnMS9f .icon-shape p,#mermaid-svg-E9bwpOZqVZgnMS9f .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-E9bwpOZqVZgnMS9f .icon-shape .label rect,#mermaid-svg-E9bwpOZqVZgnMS9f .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-E9bwpOZqVZgnMS9f .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-E9bwpOZqVZgnMS9f .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-E9bwpOZqVZgnMS9f :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 异步痛点
Promise 三态
then/catch/finally
链式 ajax / fs
resolve/reject 静态方法
async/await 预告
【代码注释】
- 左到右对应 01~05 配套示例练习顺序(§0.4)。
- 链式调用是本章核心产出:消灭回调嵌套。
0.6 配套可运行示例(一分钟自检)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>Promise 自检</title></head>
<body>
<script>
Promise.resolve('ok').then(v => console.log('本章环境正常', v));
</script>
</body>
</html>
【代码注释】
- 浏览器打开即打印则说明可运行 HTML 示例环境正常;再按 §0.4 打开 01~05 目录页面。
0.1 案例覆盖清单
| 目录 / 文件 | 知识点 | 本文章节 |
|---|---|---|
| 01-Promise对象/01-创建promise对象.html | executor 同步执行、resolve/reject 为函数 |
§2.4、§4 |
| 01/02-改变promise对象的状态.html | 状态不可逆、PromiseResult | §3 |
| 01/03-为promise对象设置回调函数.html | then 双回调、异步执行 |
§5.1 |
| 01/04-封装定时器Promise函数.html | setTimeoutPromise |
§7.2 |
| 01/05-封装ajax promise 函数.html + ajax-promise.js | XHR 返回 Promise | §7.1 |
| 01/ajax.js | Ajax 进阶篇 回调版 ajax,供对比 |
§6.4、§7.1 |
| 02-Promise的实例的方法/01-then方法.html | then 返回值四种情况 | §5.2 |
| 02/02-catch和finally.html | then+catch+finally | §5.3~5.4 |
| 03-Ajax回调地狱/ (含 ajax.js 、ajax-promise.js) | 与 01 目录同款封装 | §6.4 |
| 03/01-回调地狱写法.html | 三层嵌套 ajax({ success }) |
§6.4 |
| 03/02-then链式调用解决回调地狱.html | return ajaxPromise 扁平链 |
§6.4 |
| 03/03-then和catch一起使用解决回调地狱.html | 链尾单一 catch | §6.4 |
| 04/01-按照顺序读取文件-回调地狱写法.js | 嵌套 fs.readFile |
§6.5 |
| 04/02-按照顺序读取文件promise链式调用.js | node:fs/promises 链 |
§6.5 |
| 04/data1.txt~data3.txt | Node 读文件练习数据 | §6.5 |
| 05/01-resolve方法.html 、02-reject方法.html | 静态方法四种参数 / reject | §8 |
| 作业.md | 新歌榜 3779629 前十首歌名+歌手 |
§6.4.1 |
| 网易云音乐API.md | 接口基址与文档链接(延伸阅读) | §6.4 |
0.2 核心名词速查
| 术语 | 一句话解释 |
|---|---|
| executor | new Promise((resolve, reject) => {}) 里同步执行的函数 |
| PromiseResult | resolve/reject 传入的值,then/catch 形参收到 |
| fulfilled / resolved | 成功态(课程部分资料写 resolved) |
| rejected | 失败态 |
| then | 注册成功/失败回调,返回新 Promise |
| catch | 失败回调语法糖,支持异常穿透 |
| finally | 无论成败都执行,常用于收尾 |
| 链式调用 | return 下一异步,消灭回调嵌套 |
0.3 与 Ajax 基础篇 / Ajax 进阶篇 的衔接
- Ajax 基础篇 :XHR 五步、
onload/onerror回调。 - Ajax 进阶篇 :
ajax({ success, error })回调版;记账本多次请求。 - 本篇 :XHR 改为
ajaxPromise()返回 Promise ,用.then().catch()串联多接口(03-Ajax回调地狱 示例)。 - async/await 进阶(预告):语法糖建立在 then 返回值规则之上。
0.4 建议练习路线
| 顺序 | 目录 | 方式 | 验证点 |
|---|---|---|---|
| ① | 01-Promise对象 | 浏览器打开 01~05 的 HTML |
executor、状态、then 双参 |
| ② | 02-Promise的实例的方法 | 打开 01-then、02-catch和finally |
then 返回值、catch 穿透、finally |
| ③ | 03-Ajax回调地狱 | 打开 HTML,需外网 API | 嵌套 ajax → return ajaxPromise 链 |
| ④ | 04-Node回调地狱 | node 运行两个 .js |
回调嵌套 vs fs.promises 链 |
| ⑤ | 05-Promise类本身的方法 | 打开 resolve / reject HTML | 四种参数情形 |
| ⑥ | 延伸练习 | 在 03 目录新建页面或控制台 | 新歌榜前十首,见 §6.4.1 |
Ajax 案例注意: HTML 通过 <script src="./ajax-promise.js"> 引入封装;基址 http://api.fuming.site:54255(与 作业.md 、网易云音乐API.md 一致)。
Node 案例注意: 在 04-Node回调地狱 下执行:
bash
node 01-按照顺序读取文件-回调地狱写法.js
node 02-按照顺序读取文件promise链式调用.js
【代码注释】
- 在 04-Node回调地狱 目录执行;对比回调版与 Promise 链版控制台输出。
- 需本机安装 Node.js;
data1.txt~data3.txt与脚本同目录。
1. 异步编程概述
1.1 同步与异步
同步操作:按照代码顺序执行,前一个操作完成后才会执行下一个操作。
javascript
// 同步代码示例
console.log('1. 开始');
console.log('2. 执行中');
console.log('3. 结束');
// 执行顺序:1 -> 2 -> 3
【代码注释】
- 同步代码按书写顺序执行;阻塞主线程直到当前脚本段结束。
异步操作:不阻塞代码执行,可以在等待某些操作完成时继续执行其他代码。
javascript
// 异步代码示例
console.log('1. 开始');
setTimeout(() => {
console.log('2. 定时器回调');
}, 1000);
console.log('3. 结束');
// 执行顺序:1 -> 3 -> 2(定时器回调在主线程空闲时执行)
【代码注释】
- 宏任务:整段同步先打完 1、3,再进任务队列执行定时器打印 2。
- 理解事件循环是读懂 then 微任务顺序的前提(async/await 进阶 会展开)。
1.2 异步编程的场景
常见异步操作:
javascript
const asyncOperations = {
// 1. 网络请求
networkRequest: 'fetch(), XMLHttpRequest, axios等',
// 2. 文件操作
fileOperations: 'Node.js中的fs.readFile()等',
// 3. 定时器
timers: 'setTimeout(), setInterval()等',
// 4. 事件处理
eventHandling: '用户点击、键盘输入等事件',
// 5. 动画效果
animations: 'CSS动画、JavaScript动画等'
};
console.log('常见异步操作:', asyncOperations);
【代码注释】
- 回调地狱:嵌套深、错误难统一处理;Promise 链式是主要替代方案。
- Ajax 三接口串联见 03-01-回调地狱 vs 03-02-then链式。
1.3 回调函数的问题
回调地狱示例:
javascript
// 多层嵌套的回调函数(回调地狱)
getData(function(a) {
getMoreData(a, function(b) {
getMoreData(b, function(c) {
getMoreData(c, function(d) {
// 继续嵌套...
});
});
});
});
// 问题:
// 1. 代码嵌套过深,难以阅读
// 2. 错误处理困难
// 3. 代码复用困难
// 4. 调试困难
【代码注释】
- 回调地狱:嵌套深、错误难统一处理;Promise 链式是主要替代方案。
- Ajax 三接口串联见 03-01-回调地狱 vs 03-02-then链式。
【实战要点】
- 先分清「同步代码」「宏任务(setTimeout)」「微任务(then)」的执行顺序,再写链式调用。
- 回调版 ajax 能跑通后,再改
ajaxPromise,便于对比错误处理方式。
1.4 配套可运行示例(同步 vs 异步)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>同步异步对比</title></head>
<body>
<script>
console.log('A');
setTimeout(() => console.log('B 宏任务'), 0);
Promise.resolve().then(() => console.log('C 微任务'));
console.log('D');
</script>
</body>
</html>
【代码注释】
- 典型输出 A → D → C → B:理解微任务先于宏任务,为 then 顺序打基础。
【面试考点】
Q1:什么是回调地狱?Promise 链如何解决?
A:多层嵌套回调难读难维护;每步 return Promise 扁平化。
Q2:同步与异步在 Event Loop 中的顺序?
A:同步先执行;微任务(then)先于宏任务(setTimeout)。
【本章小结】
| 概念 | 要点 |
|---|---|
| 同步 | 按行阻塞执行 |
| 异步 | 定时器、XHR、事件不阻塞后续代码 |
| 回调地狱 | 嵌套深、错误分散 |
1.5 配套可运行示例(三层嵌套回调)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>嵌套回调</title></head>
<body>
<script>
const api = (id, cb) => setTimeout(() => cb(null, id + '-ok'), 50);
api('a', (_, a) => api('b', (_, b) => api('c', (_, c) => console.log(a, b, c))));
</script>
</body>
</html>
【代码注释】
- 模拟 03-01-回调地狱写法.html ;§6 用
return ajaxPromise改写为链式。
2. Promise 基本概念
2.1 Promise 是什么?
Promise 是 JavaScript 中处理异步操作的对象,代表一个异步操作的最终完成或失败。
名词解析:
- Promise:承诺,表示一个异步操作的最终结果
- Pending:进行中,初始状态
- Fulfilled:已成功,操作成功完成
- Rejected:已失败,操作失败
- Resolve:解决,将Promise状态改为成功
- Reject:拒绝,将Promise状态改为失败
2.2 Promise 的特点
Promise 的三大特点:
javascript
// 1. 对象的状态不受外界影响
const promise = new Promise((resolve, reject) => {
// 只有异步操作的结果可以决定当前状态
// 外界无法改变状态
});
// 2. 状态一旦改变就不会再变
const promise1 = new Promise((resolve, reject) => {
resolve('成功');
reject('失败'); // 无效,状态已经改变
});
// 3. 一旦状态改变就会触发相应的回调
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('数据');
// 状态变为 fulfilled,自动触发 then() 回调
}, 1000);
});
【代码注释】
- 结合本节标题在控制台逐步执行;注意 executor 同步、then 异步。
- 状态一旦改变不可再变;错误应沿链用 catch 统一处理。
2.3 Promise 的基本使用
创建 Promise 对象:
javascript
// 基础语法
const promise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
const success = true;
if (success) {
// 操作成功,调用 resolve
resolve('操作成功的结果');
} else {
// 操作失败,调用 reject
reject(new Error('操作失败的原因'));
}
}, 1000);
});
// 使用 Promise
promise
.then(result => {
console.log('成功:', result);
})
.catch(error => {
console.error('失败:', error);
});
【代码注释】
then/catch在状态落定后作为微任务执行,不阻塞后续同步代码。- 对应 01/03-为promise对象设置回调函数.html。
2.4 配套示例:创建 Promise 与 executor
与 01-创建promise对象.html 一致的三点结论:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>创建 Promise</title></head>
<body>
<script>
const p1 = new Promise(() => {});
console.log('空 executor,状态 pending:', p1);
const p2 = new Promise(() => { console.log('executor 同步执行'); });
console.log('p2 创建后立刻打印');
const p3 = new Promise((resolve, reject) => {
console.log('resolve、reject 类型:', typeof resolve, typeof reject);
});
</script>
</body>
</html>
【代码注释】
new Promise(executor)时 executor 立即同步执行。resolve/reject各只能有效调用一次;重复调用被忽略。- 控制台查看 Promise 对象初始为 pending。
【实战要点】
- 耗时异步应写在 executor 内,在回调里调用 resolve/reject。
- executor 内
throw等价reject(err)。
【面试考点】
Q1:Promise 构造函数参数何时执行?
A:创建时 executor 同步执行 。
Q2:resolve 多次调用会怎样?
A:仅第一次有效,状态不可逆。
2.5 配套可运行示例(状态不可逆)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>resolve 一次</title></head>
<body>
<script>
const p = new Promise((resolve, reject) => {
resolve('先成功');
reject('后失败'); // 无效
});
p.then(v => console.log(v)).catch(e => console.log(e));
</script>
</body>
</html>
【代码注释】
- 对应 02-改变promise对象的状态.html 核心结论。
【本章小结】
| 要点 | 说明 |
|---|---|
| executor | 创建时同步执行 |
| 三特点 | 外界不可改态、只变一次、触发回调 |
3. Promise 状态详解
3.1 三种状态
#mermaid-svg-9wQQzzZ4sJKwPYpv{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-9wQQzzZ4sJKwPYpv .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9wQQzzZ4sJKwPYpv .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9wQQzzZ4sJKwPYpv .error-icon{fill:#552222;}#mermaid-svg-9wQQzzZ4sJKwPYpv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9wQQzzZ4sJKwPYpv .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9wQQzzZ4sJKwPYpv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9wQQzzZ4sJKwPYpv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9wQQzzZ4sJKwPYpv .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9wQQzzZ4sJKwPYpv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9wQQzzZ4sJKwPYpv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9wQQzzZ4sJKwPYpv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9wQQzzZ4sJKwPYpv .marker.cross{stroke:#333333;}#mermaid-svg-9wQQzzZ4sJKwPYpv svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9wQQzzZ4sJKwPYpv p{margin:0;}#mermaid-svg-9wQQzzZ4sJKwPYpv defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-9wQQzzZ4sJKwPYpv g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-9wQQzzZ4sJKwPYpv g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-9wQQzzZ4sJKwPYpv g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-9wQQzzZ4sJKwPYpv g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-9wQQzzZ4sJKwPYpv g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-9wQQzzZ4sJKwPYpv .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-9wQQzzZ4sJKwPYpv .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-9wQQzzZ4sJKwPYpv .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-9wQQzzZ4sJKwPYpv .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-9wQQzzZ4sJKwPYpv .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-9wQQzzZ4sJKwPYpv .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-9wQQzzZ4sJKwPYpv .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-9wQQzzZ4sJKwPYpv .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9wQQzzZ4sJKwPYpv .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-9wQQzzZ4sJKwPYpv .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9wQQzzZ4sJKwPYpv .edgeLabel .label text{fill:#333;}#mermaid-svg-9wQQzzZ4sJKwPYpv .label div .edgeLabel{color:#333;}#mermaid-svg-9wQQzzZ4sJKwPYpv .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-9wQQzzZ4sJKwPYpv .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-9wQQzzZ4sJKwPYpv .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-9wQQzzZ4sJKwPYpv .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-9wQQzzZ4sJKwPYpv .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-9wQQzzZ4sJKwPYpv .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9wQQzzZ4sJKwPYpv .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9wQQzzZ4sJKwPYpv #statediagram-barbEnd{fill:#333333;}#mermaid-svg-9wQQzzZ4sJKwPYpv .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9wQQzzZ4sJKwPYpv .cluster-label,#mermaid-svg-9wQQzzZ4sJKwPYpv .nodeLabel{color:#131300;}#mermaid-svg-9wQQzzZ4sJKwPYpv .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-9wQQzzZ4sJKwPYpv .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-9wQQzzZ4sJKwPYpv .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-9wQQzzZ4sJKwPYpv .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-9wQQzzZ4sJKwPYpv .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-9wQQzzZ4sJKwPYpv .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-9wQQzzZ4sJKwPYpv .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-9wQQzzZ4sJKwPYpv .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-9wQQzzZ4sJKwPYpv .note-edge{stroke-dasharray:5;}#mermaid-svg-9wQQzzZ4sJKwPYpv .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-9wQQzzZ4sJKwPYpv .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-9wQQzzZ4sJKwPYpv .statediagram-note text{fill:black;}#mermaid-svg-9wQQzzZ4sJKwPYpv .statediagram-note .nodeLabel{color:black;}#mermaid-svg-9wQQzzZ4sJKwPYpv .statediagram .edgeLabel{color:red;}#mermaid-svg-9wQQzzZ4sJKwPYpv #dependencyStart,#mermaid-svg-9wQQzzZ4sJKwPYpv #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-9wQQzzZ4sJKwPYpv .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-9wQQzzZ4sJKwPYpv :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} new Promise()
resolve(value)
reject(reason) / throw
pending
fulfilled
rejected
状态一旦改变不可逆
【代码注释】
- pending :进行中;fulfilled (课程部分资料写 resolved):成功;rejected:失败。
- 仅异步结果应调用 resolve/reject;外界不能直接改状态。
- 对应 02-改变promise对象的状态.html 中先
reject再resolve仍保持 rejected。
Promise 状态机(代码对照):
javascript
const PROMISE_STATES = {
PENDING: 'pending', // 进行中,初始状态
FULFILLED: 'fulfilled', // 已成功
REJECTED: 'rejected' // 已失败
};
// 状态转换规则:
// pending -> fulfilled (成功)
// pending -> rejected (失败)
// 状态一旦改变就不可逆
console.log('Promise 状态:', PROMISE_STATES);
【代码注释】
- 规范中 fulfilled 与部分资料里的 resolved 同指成功态。
- 无法直接读
promise.status(ES202+ 有提案);用 then/catch 响应。
3.2 状态转换示例
状态变化演示:
javascript
// 成功的 Promise
const successPromise = new Promise((resolve, reject) => {
console.log('1. 初始状态: pending');
setTimeout(() => {
console.log('2. 调用 resolve,状态变为: fulfilled');
resolve('成功结果');
}, 1000);
});
successPromise.then(result => {
console.log('3. 接收结果:', result);
});
// 失败的 Promise
const failPromise = new Promise((resolve, reject) => {
console.log('1. 初始状态: pending');
setTimeout(() => {
console.log('2. 调用 reject,状态变为: rejected');
reject(new Error('失败原因'));
}, 1000);
});
failPromise.catch(error => {
console.error('3. 接收错误:', error.message);
});
【代码注释】
- resolve/reject 在异步回调里调用,状态在 then 注册之后才可能改变。
- 对应 02-改变promise对象的状态.html 中
reject后再resolve无效。
3.3 状态检查
检查 Promise 状态:
javascript
function checkPromiseState(promise) {
// 注意:无法直接检查 Promise 的状态
// Promise 的状态是内部的,不对外暴露
// 我们只能通过 then() 和 catch() 来响应状态变化
promise
.then(() => {
console.log('Promise 状态为: fulfilled');
})
.catch(() => {
console.log('Promise 状态为: rejected');
});
}
// 使用示例
const promise = new Promise((resolve) => {
setTimeout(() => resolve('完成'), 1000);
});
checkPromiseState(promise);
【代码注释】
- 演示:只能通过 then/catch 间接感知状态,不能轮询内部字段。
- 调试可用
Promise.resolve(p).then(...).catch(...)包装日志。
3.4 配套可运行示例(状态不可逆)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>Promise 状态</title></head>
<body>
<script>
const p = new Promise((resolve, reject) => {
reject('失败');
resolve('成功'); // 无效
});
p.then(v => console.log('then', v)).catch(e => console.log('catch', e));
</script>
</body>
</html>
【代码注释】
- 对应 02-改变promise对象的状态.html;先 reject 则永远 rejected。
【本章小结】
| 状态 | 含义 |
|---|---|
| pending | 初始,未落定 |
| fulfilled | resolve 成功 |
| rejected | reject / throw 失败 |
【实战要点】
- 状态只变一次;
reject后再resolve无效。 - 不能轮询内部状态字段,用 then/catch 响应。
【面试考点】
Q1:Promise 有哪几种状态?能否从 fulfilled 回到 pending?
A:pending / fulfilled / rejected;不可逆。
3.5 配套可运行示例(pending → fulfilled)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>状态变化</title></head>
<body>
<script>
const p = new Promise(resolve => setTimeout(() => resolve('完成'), 500));
console.log('创建后立刻打印,仍为 pending');
p.then(v => console.log('then 收到', v));
</script>
</body>
</html>
【代码注释】
- 500ms 后 resolve;观察控制台先打 pending 提示,再打 then 结果。
4. Promise 基础语法
4.1 Promise 构造函数
创建 Promise 对象:
javascript
// 基础创建方式
const promise1 = new Promise((resolve, reject) => {
// executor 函数,在创建 Promise 时立即执行
console.log('Promise 创建时立即执行');
// 异步操作
setTimeout(() => {
resolve('成功');
}, 1000);
});
// 包装异步操作
function readFileAsync(filename) {
return new Promise((resolve, reject) => {
// 模拟异步读取文件
setTimeout(() => {
const success = Math.random() > 0.3;
if (success) {
resolve(`文件 ${filename} 的内容`);
} else {
reject(new Error(`读取文件 ${filename} 失败`));
}
}, 1000);
});
}
// 使用示例
readFileAsync('data.txt')
.then(content => {
console.log('文件内容:', content);
})
.catch(error => {
console.error('读取失败:', error.message);
});
【代码注释】
- executor 立即执行;把
fs.readFile、XHR 等包进 Promise 是常见模式。 - Node 现代写法用
fs.promises或util.promisify,见 §6.4。
4.2 resolve 和 reject
resolve 和 reject 的使用:
javascript
// resolve 的使用
const promise1 = new Promise((resolve) => {
// resolve 可以传递各种类型的值
resolve('字符串值');
// resolve(123); // 数字
// resolve({ name: '对象' }); // 对象
// resolve([1, 2, 3]); // 数组
// resolve(anotherPromise); // 另一个 Promise
});
// reject 的使用
const promise2 = new Promise((_, reject) => {
// reject 通常传递 Error 对象
reject(new Error('操作失败'));
// reject('错误原因'); // 也可以传递其他类型
});
// 实际应用示例
function getUserData(userId) {
return new Promise((resolve, reject) => {
// 模拟 API 调用
setTimeout(() => {
if (userId > 0) {
resolve({
id: userId,
name: '用户' + userId,
email: `user${userId}@example.com`
});
} else {
reject(new Error('无效的用户ID'));
}
}, 1000);
});
}
// 使用示例
getUserData(1)
.then(user => {
console.log('用户信息:', user);
})
.catch(error => {
console.error('错误:', error.message);
});
【代码注释】
- resolve 可传任意类型作 PromiseResult;reject 建议用 Error 便于堆栈。
- executor 里
throw等价于reject(err)。
4.3 配套可运行示例(then 双回调)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>then 双参</title></head>
<body>
<script>
const ok = new Promise(r => setTimeout(() => r('OK'), 200));
const bad = new Promise((_, j) => setTimeout(() => j('ERR'), 200));
ok.then(v => console.log('成功', v), e => console.log('失败', e));
bad.then(v => console.log('成功', v), e => console.log('失败', e));
</script>
</body>
</html>
【代码注释】
- 对应 03-为promise对象设置回调函数.html ;更推荐失败分支用
.catch()。
【本章小结】
| API | 时机 |
|---|---|
| executor | new Promise 时同步执行 |
| resolve/reject | 异步结果落定 |
【实战要点】
- 把 XHR、
fs.readFile包进 executor;Node 优先fs.promises。 - executor 内
throw等价reject。
【面试考点】
Q1:resolve 可以传什么?
A:任意类型;传 thenable 会按规范展开。
5. Promise 实例方法
5.1 then() 方法
5.1.1 配套可运行示例(基础 then)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>基础 then</title></head>
<body>
<script>
new Promise((resolve) => setTimeout(() => resolve('数据'), 300))
.then(v => console.log('成功', v))
.catch(e => console.error('失败', e));
</script>
</body>
</html>
【代码注释】
- 对应 03-为promise对象设置回调函数.html;then 在 resolve 后作为微任务执行。
then() 方法详解:
javascript
// then() 方法的基础语法
promise.then(
onFulfilled, // Promise 成功时的回调函数
onRejected // Promise 失败时的回调函数(可选)
);
// 基础使用
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('成功数据');
}, 1000);
});
// 方式一:只处理成功情况
promise.then(result => {
console.log('成功:', result);
});
// 方式二:同时处理成功和失败
promise.then(
result => {
console.log('成功:', result);
},
error => {
console.error('失败:', error);
}
);
// 方式三:链式调用,使用 catch() 处理错误
promise
.then(result => {
console.log('第一步成功:', result);
return result + ' -> 处理';
})
.then(result => {
console.log('第二步成功:', result);
})
.catch(error => {
console.error('任何步骤失败:', error);
});
【代码注释】
- then 两个参数分别处理成功/失败;更推荐成功用 then、失败用 catch。
- then 回调返回值决定下一个 then 收到的 Promise 状态与结果。
5.2 then() 返回值规则
then() 的返回值:
javascript
// 规则1:返回普通值,状态变为 fulfilled
Promise.resolve(1)
.then(value => {
console.log('接收到:', value); // 1
return value + 1; // 返回 2
})
.then(value => {
console.log('接收到:', value); // 2
});
// 规则2:返回 Promise,状态取决于返回的 Promise
Promise.resolve()
.then(() => {
return new Promise(resolve => {
setTimeout(() => resolve('异步结果'), 1000);
});
})
.then(value => {
console.log('异步结果:', value);
});
// 规则3:抛出错误,状态变为 rejected
Promise.resolve()
.then(() => {
throw new Error('手动抛出的错误');
})
.then(() => {
console.log('不会执行');
})
.catch(error => {
console.error('捕获错误:', error.message);
});
// 规则4:没有返回值,等同于返回 undefined
Promise.resolve(1)
.then(value => {
console.log('处理:', value);
// 没有 return 语句
})
.then(value => {
console.log('接收到:', value); // undefined
});
【代码注释】
- 规范四种情况:无返回→undefined 成功;返回值→成功;返回 Promise→跟随;throw→失败。
- 对应 02-01-then方法.html 对 then 返回 Promise 的实验。
5.3 catch() 方法
catch() 方法详解:
javascript
// catch() 是 then(null, rejection) 的语法糖
const promise = new Promise((resolve, reject) => {
reject(new Error('操作失败'));
});
// 方式一:使用 then 的第二个参数
promise.then(
result => {
console.log('成功:', result);
},
error => {
console.error('失败:', error);
}
);
// 方式二:使用 catch()(推荐)
promise
.then(result => {
console.log('成功:', result);
})
.catch(error => {
console.error('失败:', error);
});
// catch() 的错误捕获特性
Promise.resolve()
.then(() => {
throw new Error('第一步错误');
})
.then(() => {
console.log('不会执行');
})
.catch(error => {
console.error('捕获错误:', error.message);
return '继续执行';
})
.then(result => {
console.log('继续执行:', result); // 会执行
});
【代码注释】
- catch 等价
then(null, rejection);可捕获链上任意 then 抛错。 - catch 返回非 throw 的值会让后续 then 继续走成功分支。
5.4 finally() 方法
finally() 方法详解:
javascript
// finally() 无论成功或失败都会执行
const promise = new Promise((resolve) => {
setTimeout(() => resolve('成功'), 1000);
});
promise
.then(result => {
console.log('成功:', result);
return result;
})
.catch(error => {
console.error('失败:', error);
throw error;
})
.finally(() => {
console.log('清理资源,关闭连接');
// finally 不接收参数,无法知道 Promise 的状态
});
// 实际应用:加载状态管理
function loadDataWithLoading() {
let isLoading = true;
updateLoadingIndicator(isLoading);
fetchData()
.then(data => {
displayData(data);
})
.catch(error => {
showError(error);
})
.finally(() => {
isLoading = false;
updateLoadingIndicator(isLoading);
});
}
function updateLoadingIndicator(loading) {
if (loading) {
console.log('显示加载指示器');
} else {
console.log('隐藏加载指示器');
}
}
【代码注释】
- finally 不接收结果参数,适合做关闭 loading、释放资源。
- 对应 02-catch和finally.html;与 try/finally 语义类似。
5.5 配套可运行示例(then 返回值)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>then 返回值</title></head>
<body>
<script>
Promise.resolve(1)
.then(v => { console.log(1, v); return v + 1; })
.then(v => { console.log(2, v); return new Promise(r => setTimeout(() => r(99), 200)); })
.then(v => console.log(3, v));
</script>
</body>
</html>
【代码注释】
- 对应 02-01-then方法.html;返回 Promise 时下一 then 等待其落定。
5.6 配套可运行示例(catch + finally)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>catch finally</title></head>
<body>
<script>
Promise.reject('网络错误')
.catch(e => { console.log('catch', e); return '默认值'; })
.then(v => console.log('恢复', v))
.finally(() => console.log('收尾'));
</script>
</body>
</html>
【代码注释】
- catch 返回值让后续 then 走成功分支;finally 不接收结果参数。
【本章小结】
| 方法 | 作用 |
|---|---|
| then | 注册回调,返回新 Promise |
| catch | 失败语法糖,可恢复链 |
| finally | 无论成败收尾 |
【实战要点】
- then 返回普通值 / Promise / throw 决定下一节状态。
- 推荐成功
then、失败统一catch。
【面试考点】
Q1:then 返回 undefined 时下一 then 收到什么?
A:fulfilled 且结果为 undefined。
6. Promise 链式调用
6.1 链式调用原理
链式调用详解:
javascript
// Promise 链式调用原理
Promise.resolve(1)
.then(value => {
console.log('第一步:', value); // 1
return value + 1;
})
.then(value => {
console.log('第二步:', value); // 2
return value + 1;
})
.then(value => {
console.log('第三步:', value); // 3
return value + 1;
})
.then(value => {
console.log('最终结果:', value); // 4
});
// 链式调用解决回调地狱
// 传统回调方式:
asyncOperation1(data1 => {
asyncOperation2(data1, data2 => {
asyncOperation3(data2, data3 => {
asyncOperation4(data3, data4 => {
// 继续嵌套...
});
});
});
});
// Promise 链式调用:
asyncOperation1()
.then(data1 => asyncOperation2(data1))
.then(data2 => asyncOperation3(data2))
.then(data3 => asyncOperation4(data3))
.then(data4 => {
// 最终处理
});
【代码注释】
- 每个 then 返回新 Promise,才能
.then().then()。 - 对比嵌套 ajax 与
return ajaxPromise()扁平链(§6.4)。
6.4 配套示例:Ajax 回调地狱 → Promise 链
6.4.2 配套可运行示例(链尾 catch)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>链尾 catch</title></head>
<body>
<script>
Promise.resolve()
.then(() => { throw new Error('中间出错'); })
.then(() => console.log('不会执行'))
.catch(e => console.log('链尾捕获', e.message));
</script>
</body>
</html>
【代码注释】
- 对应 03-then和catch一起使用解决回调地狱.html 的异常穿透思路。
三步接口 (03-Ajax回调地狱 ):toplist → playlist/detail → song/detail。
回调地狱结构:
javascript
ajax({ url: '/toplist', success: res => {
ajax({ url: '/playlist/detail?id=' + res.list[0].id, success: res2 => {
ajax({ url: '/song/detail?ids=' + res2.privileges[0].id, success: res3 => {
console.log(res3);
}});
}});
}});
【代码注释】
- 嵌套 success 难以统一错误处理;依赖 Ajax 进阶篇 的
ajax.js。
Promise 链(02 / 03 案例):
html
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>Ajax Promise 链</title></head>
<body>
<script>
function ajaxPromise(options) {
const { url, method = 'GET', headers = {}, body, dataType } = options;
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
if (dataType) xhr.responseType = dataType;
xhr.onload = () => xhr.status === 200 ? resolve(xhr.response) : reject({ error: 1001 });
xhr.onerror = () => reject({ error: 1002 });
xhr.open(method, url);
for (let k in headers) xhr.setRequestHeader(k, headers[k]);
xhr.send(body);
});
}
const API = 'http://api.fuming.site:54255';
ajaxPromise({ url: API + '/toplist', dataType: 'json' })
.then(res => ajaxPromise({ url: API + '/playlist/detail?id=' + res.list[0].id, dataType: 'json' }))
.then(res => ajaxPromise({ url: API + '/song/detail?ids=' + res.privileges[0].id, dataType: 'json' }))
.then(res => console.log(res))
.catch(() => console.log('数据获取失败!'));
</script>
</body>
</html>
【代码注释】
- 每步
return ajaxPromise(...)连接下一 then;03 在链尾用单一 catch(异常穿透)。 - 与 01-Promise对象/ajax-promise.js 源码一致;API 需网络,可改 mock 练语法。
#mermaid-svg-S0TtuL5aTN1RCzkB{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-S0TtuL5aTN1RCzkB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-S0TtuL5aTN1RCzkB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-S0TtuL5aTN1RCzkB .error-icon{fill:#552222;}#mermaid-svg-S0TtuL5aTN1RCzkB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-S0TtuL5aTN1RCzkB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-S0TtuL5aTN1RCzkB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-S0TtuL5aTN1RCzkB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-S0TtuL5aTN1RCzkB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-S0TtuL5aTN1RCzkB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-S0TtuL5aTN1RCzkB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-S0TtuL5aTN1RCzkB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-S0TtuL5aTN1RCzkB .marker.cross{stroke:#333333;}#mermaid-svg-S0TtuL5aTN1RCzkB svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-S0TtuL5aTN1RCzkB p{margin:0;}#mermaid-svg-S0TtuL5aTN1RCzkB .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-S0TtuL5aTN1RCzkB .cluster-label text{fill:#333;}#mermaid-svg-S0TtuL5aTN1RCzkB .cluster-label span{color:#333;}#mermaid-svg-S0TtuL5aTN1RCzkB .cluster-label span p{background-color:transparent;}#mermaid-svg-S0TtuL5aTN1RCzkB .label text,#mermaid-svg-S0TtuL5aTN1RCzkB span{fill:#333;color:#333;}#mermaid-svg-S0TtuL5aTN1RCzkB .node rect,#mermaid-svg-S0TtuL5aTN1RCzkB .node circle,#mermaid-svg-S0TtuL5aTN1RCzkB .node ellipse,#mermaid-svg-S0TtuL5aTN1RCzkB .node polygon,#mermaid-svg-S0TtuL5aTN1RCzkB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-S0TtuL5aTN1RCzkB .rough-node .label text,#mermaid-svg-S0TtuL5aTN1RCzkB .node .label text,#mermaid-svg-S0TtuL5aTN1RCzkB .image-shape .label,#mermaid-svg-S0TtuL5aTN1RCzkB .icon-shape .label{text-anchor:middle;}#mermaid-svg-S0TtuL5aTN1RCzkB .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-S0TtuL5aTN1RCzkB .rough-node .label,#mermaid-svg-S0TtuL5aTN1RCzkB .node .label,#mermaid-svg-S0TtuL5aTN1RCzkB .image-shape .label,#mermaid-svg-S0TtuL5aTN1RCzkB .icon-shape .label{text-align:center;}#mermaid-svg-S0TtuL5aTN1RCzkB .node.clickable{cursor:pointer;}#mermaid-svg-S0TtuL5aTN1RCzkB .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-S0TtuL5aTN1RCzkB .arrowheadPath{fill:#333333;}#mermaid-svg-S0TtuL5aTN1RCzkB .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-S0TtuL5aTN1RCzkB .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-S0TtuL5aTN1RCzkB .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-S0TtuL5aTN1RCzkB .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-S0TtuL5aTN1RCzkB .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-S0TtuL5aTN1RCzkB .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-S0TtuL5aTN1RCzkB .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-S0TtuL5aTN1RCzkB .cluster text{fill:#333;}#mermaid-svg-S0TtuL5aTN1RCzkB .cluster span{color:#333;}#mermaid-svg-S0TtuL5aTN1RCzkB div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-S0TtuL5aTN1RCzkB .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-S0TtuL5aTN1RCzkB rect.text{fill:none;stroke-width:0;}#mermaid-svg-S0TtuL5aTN1RCzkB .icon-shape,#mermaid-svg-S0TtuL5aTN1RCzkB .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-S0TtuL5aTN1RCzkB .icon-shape p,#mermaid-svg-S0TtuL5aTN1RCzkB .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-S0TtuL5aTN1RCzkB .icon-shape .label rect,#mermaid-svg-S0TtuL5aTN1RCzkB .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-S0TtuL5aTN1RCzkB .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-S0TtuL5aTN1RCzkB .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-S0TtuL5aTN1RCzkB :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ajaxPromise toplist
then 取 id
return playlist
then 取 songId
return song detail
then / catch
【代码注释】
- 扁平链对应流程图;缺
return会导致下一步收到 undefined。
【实战要点】
- 链上必须
returnPromise 或值;业务失败可throw进 catch。 - HTTP 200 但业务
code失败要在 then 里主动reject。
【面试考点】
- then 返回 Promise 时下一个 then 如何等待?
- 异常穿透:中间 then 抛错如何传到末尾 catch?
6.4.1 延伸练习:新歌榜前十首
配套 03 演示「榜单 → 歌单 → 单曲」三步链;作业 改为固定新歌榜 并输出前十首的歌名 + 演唱者。
| 接口 | URL |
|---|---|
| 歌单详情 | GET http://api.fuming.site:54255/playlist/detail?id=3779629 |
| 歌曲详情(批量) | GET http://api.fuming.site:54255/song/detail?ids=id1,id2,... |
思路: 先取歌单中前 10 个 privileges[].id,再一次性请求 song/detail,在 then 里遍历 songs 打印。
javascript
const API = 'http://api.fuming.site:54255';
ajaxPromise({ url: API + '/playlist/detail?id=3779629', dataType: 'json' })
.then(res => {
const ids = res.privileges.slice(0, 10).map(p => p.id).join(',');
return ajaxPromise({ url: API + '/song/detail?ids=' + ids, dataType: 'json' });
})
.then(res => {
(res.songs || []).forEach((song, i) => {
const artists = (song.ar || []).map(a => a.name).join(' / ');
console.log(`${i + 1}. ${song.name} --- ${artists}`);
});
})
.catch(() => console.log('数据获取失败'));
【代码注释】
- 与 03/02-then链式调用 相同模式:
return ajaxPromise进入下一步;链尾 catch 统一失败提示。 ids用逗号拼接为网易云 API 约定;不足 10 首时检查privileges长度。- 接口字段以实际 JSON 为准,可在 Network 面板对照 网易云音乐API.md 文档。
- 作业不要求 DOM 渲染,控制台输出即可;扩展可做列表 UI(async/await 进阶 可用 async/await 改写)。
【实战要点】
- 公共 API 可能限流或变更,练不通时先确认 03 三步链能否访问,再改作业 id。
- 前十首若需并行请求(10 次
song/detail),应等 async/await 进阶 Promise.all ;本课用单次批量ids更合适。
6.5 配套示例:Node 顺序读文件
回调地狱(01-按照顺序读取文件-回调地狱写法.js):
javascript
fs.readFile(path.resolve(__dirname, './data1.txt'), 'utf-8', (err, data) => {
console.log('data1:', data);
fs.readFile(path.resolve(__dirname, './data2.txt'), 'utf-8', (err, data) => {
console.log('data2:', data);
fs.readFile(path.resolve(__dirname, './data3.txt'), 'utf-8', (err, data) => {
console.log('data3:', data);
});
});
});
【代码注释】
- 三层
readFile嵌套,错误需每层判断err,维护成本高。 - 与 02 的 Promise 链读同一组 data1.txt~data3.txt,便于对比输出。
Promise 链(02-按照顺序读取文件promise链式调用.js):
javascript
const fs = require('node:fs/promises');
const path = require('path');
fs.readFile(path.resolve(__dirname, './data1.txt'), 'utf-8')
.then(data => {
console.log('data1:', data);
return fs.readFile(path.resolve(__dirname, './data2.txt'), 'utf-8');
})
.then(data => {
console.log('data2:', data);
return fs.readFile(path.resolve(__dirname, './data3.txt'), 'utf-8');
})
.then(data => console.log('data3:', data))
.catch(err => { throw err; });
【代码注释】
- 对比 01 三层嵌套
fs.readFile;return下一读操作形成链。 - Node 16+ 直接用
fs.promises,不必手写 promisify。
bash
cd 04-Node回调地狱
node 01-按照顺序读取文件-回调地狱写法.js
node 02-按照顺序读取文件promise链式调用.js
【代码注释】
- 在 04-Node回调地狱 目录执行;对比回调嵌套与 Promise 链输出。
- 需 Node.js;
data1.txt~data3.txt与脚本同目录。
6.2 链式调用最佳实践
链式调用技巧:
javascript
// 1. 每个 then() 返回值
Promise.resolve()
.then(() => {
return fetchData(); // 返回 Promise
})
.then(data => {
return processData(data); // 返回处理后的数据
})
.then(result => {
return saveData(result); // 返回保存结果
});
// 2. 错误处理在链的末尾
Promise.resolve()
.then(() => riskyOperation())
.then(() => anotherRiskyOperation())
.then(() => finalOperation())
.catch(error => {
// 统一错误处理
handleError(error);
});
// 3. 使用 finally() 进行清理
let resource;
Promise.resolve()
.then(() => {
resource = acquireResource();
return useResource(resource);
})
.then(result => {
return processResult(result);
})
.catch(error => {
console.error('处理失败:', error);
})
.finally(() => {
// 无论如何都要释放资源
if (resource) {
releaseResource(resource);
}
});
【代码注释】
- 结合本节标题在控制台逐步执行;注意 executor 同步、then 异步。
- 状态一旦改变不可再变;错误应沿链用 catch 统一处理。
6.3 错误传递机制
错误传递详解:
javascript
// 错误会沿着链向下传递,直到被 catch() 捕获
Promise.resolve()
.then(() => {
throw new Error('第一步错误');
})
.then(() => {
console.log('不会执行');
})
.then(() => {
console.log('也不会执行');
})
.catch(error => {
console.error('最终捕获:', error.message);
});
// 在 catch() 中返回值可以继续链式调用
Promise.resolve()
.then(() => {
throw new Error('出错啦');
})
.catch(error => {
console.error('捕获错误:', error.message);
return '默认值'; // 返回默认值
})
.then(value => {
console.log('继续执行:', value); // 会执行
});
// catch() 中抛出错误会继续传递
Promise.resolve()
.then(() => {
throw new Error('第一个错误');
})
.catch(error => {
console.error('第一个catch:', error.message);
throw new Error('第二个错误'); // 抛出新的错误
})
.catch(error => {
console.error('第二个catch:', error.message);
});
【代码注释】
- 结合本节标题在控制台逐步执行;注意 executor 同步、then 异步。
- 状态一旦改变不可再变;错误应沿链用 catch 统一处理。
6.6 配套可运行示例(链式 mock)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>链式对比</title></head>
<body>
<script>
function step(n, ms) {
return new Promise(r => setTimeout(() => r('step' + n), ms));
}
step(1, 100).then(v => { console.log(v); return step(2, 100); })
.then(v => { console.log(v); return step(3, 100); })
.then(v => console.log(v))
.catch(e => console.error(e));
</script>
</body>
</html>
【代码注释】
- 本地 mock 三步;对照 03-01-回调地狱写法.html 嵌套写法。
【本章小结】
| 模式 | 要点 |
|---|---|
| 链式 | 每步 return Promise 或值 |
| Ajax | return ajaxPromise 扁平多接口 |
| Node | fs.promises + return 下一读 |
【实战要点】
- 链尾单一 catch;HTTP 200 但业务失败需主动 reject。
- 延伸练习:歌单
3779629→ 批量song/detail(§6.4.1)。
【面试考点】
Q1:链中间忘记 return 会怎样?
A:下一 then 收到 undefined,后续逻辑易错。
7. Promise 常见应用
7.1 网络请求封装
配套 ajax-promise.js(与 Ajax 进阶篇 ajax.js 对照):
javascript
function ajaxPromise(options) {
const { url, method = 'GET', headers = {}, body, dataType } = options;
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
if (dataType) xhr.responseType = dataType;
xhr.onload = () => {
if (xhr.status === 200) resolve(xhr.response);
else reject({ error: 1001, msg: '未能获取到正确的内容!' });
};
xhr.onerror = () => reject({ error: 1002, msg: '未能成功发送请求!' });
xhr.open(method, url);
for (let key in headers) xhr.setRequestHeader(key, headers[key]);
xhr.send(body);
});
}
【代码注释】
- 与 05-封装ajax promise 函数.html 引入的脚本一致;用
return new Promise替代 success/error 回调。 - 调用方:
ajaxPromise({ url, dataType:'json' }).then(...).catch(...)。 - 链式多接口见 §6.4(02-then链式调用解决回调地狱 、03-then和catch一起使用)。
通用 httpGet 示例(理解原理):
javascript
// 封装 XMLHttpRequest
function httpGet(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onload = function() {
if (xhr.status === 200) {
try {
const data = JSON.parse(xhr.responseText);
resolve(data);
} catch (error) {
reject(error);
}
} else {
reject(new Error(`HTTP ${xhr.status}`));
}
};
xhr.onerror = function() {
reject(new Error('网络错误'));
};
xhr.send();
});
}
// 使用示例
httpGet('/api/users')
.then(users => {
console.log('用户列表:', users);
})
.catch(error => {
console.error('获取失败:', error);
});
// 封装 POST 请求
function httpPost(url, data) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('POST', url);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onload = function() {
if (xhr.status === 201) {
try {
const response = JSON.parse(xhr.responseText);
resolve(response);
} catch (error) {
reject(error);
}
} else {
reject(new Error(`HTTP ${xhr.status}`));
}
};
xhr.onerror = function() {
reject(new Error('网络错误'));
};
xhr.send(JSON.stringify(data));
});
}
// 使用示例
httpPost('/api/users', {
name: '张三',
email: 'zhangsan@example.com'
})
.then(response => {
console.log('创建成功:', response);
})
.catch(error => {
console.error('创建失败:', error);
});
【代码注释】
- 配套 ajax-promise.js:200 resolve 响应体,否则 reject 自定义对象。
- 与 Ajax 进阶篇 回调版
ajax({success,error})对照,接口改为返回 Promise。
7.2 定时器封装
配套 setTimeoutPromise(04-封装定时器Promise函数.html):
javascript
function setTimeoutPromise(delay) {
return new Promise(resolve => {
setTimeout(resolve, delay);
});
}
setTimeoutPromise(3000).then(() => console.log('Promise 版定时器执行了'));
【代码注释】
resolve无参时 then 收到undefined;延迟时间单位毫秒。- 原版
setTimeout与 Promise 版可并行对比执行先后。 - async/await 进阶 可写
await setTimeoutPromise(3000)。
Promise 版本的 setTimeout(通用写法):
javascript
// Promise 版本的 setTimeout
function delay(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
// 使用示例
async function exampleWithDelay() {
console.log('开始');
await delay(1000);
console.log('1秒后');
await delay(2000);
console.log('再过2秒');
}
// Promise 版本的 setInterval
function interval(ms, callback) {
return new Promise(resolve => {
const id = setInterval(() => {
const result = callback();
if (result === 'stop') {
clearInterval(id);
resolve();
}
}, ms);
});
}
// 使用示例
async function timerExample() {
let count = 0;
await interval(1000, () => {
count++;
console.log('计数:', count);
if (count >= 5) {
console.log('停止计时');
return 'stop';
}
});
console.log('计时结束');
}
【代码注释】
new Promise(r => setTimeout(r, ms))是标准延迟模式。- 对应 04-封装定时器Promise函数.html。
7.3 文件操作封装
Node.js 文件操作 Promise 化:
javascript
const fs = require('fs');
// Promise 版本的 readFile
function readFilePromise(filename) {
return new Promise((resolve, reject) => {
fs.readFile(filename, 'utf8', (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
// Promise 版本的 writeFile
function writeFilePromise(filename, content) {
return new Promise((resolve, reject) => {
fs.writeFile(filename, content, 'utf8', (err) => {
if (err) {
reject(err);
} else {
resolve('文件写入成功');
}
});
});
}
// 使用示例
async function processFile() {
try {
// 读取文件
const content = await readFilePromise('input.txt');
console.log('文件内容:', content);
// 处理内容
const processed = content.toUpperCase();
// 写入文件
await writeFilePromise('output.txt', processed);
console.log('文件处理完成');
} catch (error) {
console.error('文件操作失败:', error);
}
}
【代码注释】
- 手动 promisify 教学用;Node 16+ 推荐
fs.promises.readFile(§6.4)。 - 链式读文件避免三层嵌套回调。
7.4 配套可运行示例(定时器 Promise)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>定时器 Promise</title></head>
<body>
<script>
function setTimeoutPromise(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
console.log('start');
setTimeoutPromise(800).then(() => console.log('after 800ms'));
</script>
</body>
</html>
【代码注释】
- 对应 04-封装定时器Promise函数.html ;async/await 可
await setTimeoutPromise(ms)。
7.5 配套可运行示例(ajaxPromise 最小版)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>ajaxPromise</title></head>
<body>
<button id="btn">请求</button><pre id="out"></pre>
<script>
function ajaxPromise({ url, dataType }) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
if (dataType) xhr.responseType = dataType;
xhr.onload = () => xhr.status === 200 ? resolve(xhr.response) : reject('HTTP');
xhr.onerror = () => reject('NET');
xhr.open('GET', url);
xhr.send();
});
}
document.getElementById('btn').onclick = () => {
ajaxPromise({ url: 'https://httpbin.org/get', dataType: 'json' })
.then(d => out.textContent = JSON.stringify(d, null, 2))
.catch(e => out.textContent = String(e));
};
const out = document.getElementById('out');
</script>
</body>
</html>
【代码注释】
- 对应 05-封装ajax promise 函数.html;课堂 API 见 §6.4。
【本章小结】
| 封装 | 场景 |
|---|---|
| ajaxPromise | XHR → Promise |
| setTimeoutPromise | 延迟 |
| promisify | Node 回调 → Promise |
【实战要点】
- ajax-promise.js 与回调版 ajax.js 对照学错误处理。
- 定时器 Promise 是
await delay(ms)的基础。
【面试考点】
Q1:如何把回调 API 改成 Promise?
A:在回调里调用 resolve/reject,并 return new Promise(...)。
8. Promise 静态方法
8.1 Promise.resolve()
功能:返回一个 Promise,状态由参数决定(课程笔记 §4.1)。
javascript
// 情况一:无参 → fulfilled,结果为 undefined
Promise.resolve().then(v => console.log(v)); // undefined
// 情况二:非 Promise、非 thenable 的值 → fulfilled,结果为该值
Promise.resolve(100).then(v => console.log(v)); // 100
// 情况三:参数已是 Promise → 原样返回(同一对象)
const p1 = new Promise((resolve, reject) => {
Math.random() >= 0.5 ? resolve('ok') : reject('fail');
});
const p2 = Promise.resolve(p1); // 等价 const p2 = p1
// 情况四:thenable 对象(有 then 方法)→ 执行 then,按其 resolve/reject 定状态
const obj = {
then(res, rej) {
Math.floor(Math.random() * 10) >= 5 ? res(8) : rej(3);
}
};
Promise.resolve(obj).then(v => console.log('成功', v)).catch(r => console.log('失败', r));
【代码注释】
- 对应 05-01-resolve方法.html 四种注释情形。
- thenable 常用于兼容第三方「类 Promise」对象。
Promise.resolve(p1)不会多包一层,与new Promise(r => r(p1))不同。
8.2 Promise.reject()
8.2.1 配套可运行示例(仅 reject)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>Promise.reject</title></head>
<body>
<script>
Promise.reject(new Error('直接失败'))
.then(() => console.log('跳过'))
.catch(e => console.log('catch', e.message));
</script>
</body>
</html>
【代码注释】
- 对应 05-02-reject方法.html;无需 new Promise 即可得到 rejected 实例。
javascript
Promise.reject('出错了')
.then(() => console.log('不会执行'))
.catch(reason => console.log('失败', reason)); // 出错了
【代码注释】
- 直接得到 rejected 状态,PromiseResult 为参数。
- 对应 05-02-reject方法.html ;等价
new Promise((_, rej) => rej('出错了'))。 - 规范中还有
Promise.all/race/allSettled在后续课程展开,本篇 以 resolve/reject 为主。
【实战要点】
- 已知同步成功值用
Promise.resolve(data)比new Promise(r => r(data))更简洁。 - 快速失败路径用
Promise.reject(err)进入统一 catch。
8.3 配套可运行示例(Promise.resolve / reject)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>静态方法</title></head>
<body>
<script>
Promise.resolve(100).then(v => console.log('resolve 100', v));
Promise.resolve({ then(res) { res('thenable'); } }).then(v => console.log('thenable', v));
Promise.reject('fail').catch(e => console.log('reject', e));
</script>
</body>
</html>
【代码注释】
- 对应 05-01-resolve方法.html 、05-02-reject方法.html 核心情形。
【面试考点】
Promise.resolve(thenable)与Promise.resolve(普通值)区别?Promise.reject与 executor 里reject的关系?
【本章小结】
| 方法 | 结果 |
|---|---|
| Promise.resolve | 四种参数情形定状态 |
| Promise.reject | 直接 rejected |
9. 调试与错误处理
9.1 错误处理最佳实践
全面的错误处理:
javascript
// 1. 始终使用 catch() 处理错误
Promise.resolve()
.then(() => riskyOperation())
.catch(error => {
console.error('捕获错误:', error);
});
// 2. 在 catch() 中返回默认值
Promise.resolve()
.then(() => riskyOperation())
.catch(error => {
console.error('操作失败,使用默认值:', error);
return defaultValue; // 返回默认值
})
.then(value => {
console.log('继续执行:', value);
});
// 3. 区分错误类型
Promise.resolve()
.then(() => riskyOperation())
.catch(error => {
if (error instanceof NetworkError) {
console.error('网络错误:', error);
return fallbackData();
} else if (error instanceof ValidationError) {
console.error('验证错误:', error);
throw error; // 重新抛出验证错误
} else {
console.error('未知错误:', error);
throw error;
}
});
// 4. 使用 finally() 进行清理
let connection;
Promise.resolve()
.then(() => {
connection = createConnection();
return queryDatabase(connection);
})
.catch(error => {
console.error('查询失败:', error);
})
.finally(() => {
if (connection) {
connection.close();
}
});
【代码注释】
- 结合本节标题在控制台逐步执行;注意 executor 同步、then 异步。
- 状态一旦改变不可再变;错误应沿链用 catch 统一处理。
9.2 调试技巧
Promise 调试方法:
javascript
// 1. 在每个 then() 中添加日志
Promise.resolve()
.then(value => {
console.log('步骤1:', value);
return processValue(value);
})
.then(value => {
console.log('步骤2:', value);
return processValue(value);
})
.then(value => {
console.log('步骤3:', value);
return processValue(value);
});
// 2. 使用 Promise 包装器进行调试
function debugPromise(promise, name) {
return promise
.then(value => {
console.log(`✅ ${name} 成功:`, value);
return value;
})
.catch(error => {
console.error(`❌ ${name} 失败:`, error);
throw error;
});
}
// 使用示例
debugPromise(fetchUser('123'), '获取用户')
.then(user => {
return debugPromise(fetchUserPosts(user.id), '获取文章');
})
.then(posts => {
console.log('最终结果:', posts);
});
// 3. 添加超时检测
function withTimeout(promise, timeout, name) {
return Promise.race([
promise,
new Promise((_, reject) => {
setTimeout(() => {
reject(new Error(`${name} 超时`));
}, timeout);
})
]);
}
// 使用示例
withTimeout(fetchData(), 5000, '数据加载')
.then(data => {
console.log('数据:', data);
})
.catch(error => {
console.error('错误:', error.message);
});
【代码注释】
Promise.race可做超时;注意竞态先完成者决定结果。- 未捕获的 reject 会触发
unhandledrejection,生产应始终 catch。
9.3 配套可运行示例(链式调试)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>调试链</title></head>
<body>
<script>
function debugP(p, name) {
return p.then(v => (console.log('✅', name, v), v))
.catch(e => (console.error('❌', name, e), Promise.reject(e)));
}
debugP(Promise.resolve(1), 's1')
.then(v => debugP(Promise.resolve(v + 1), 's2'))
.then(v => console.log('done', v))
.catch(() => {});
</script>
</body>
</html>
【代码注释】
- 分步 log 定位链中失败节点;配合浏览器异步调用栈。
【本章小结】
| 实践 | 说明 |
|---|---|
| 统一 catch | 链尾或 try/catch |
| 调试 | debugP / 分步 log |
| 超时 | Promise.race(了解) |
【实战要点】
- 未捕获 reject 触发
unhandledrejection。 - catch 里 return 可恢复,throw 继续传递。
【面试考点】
Q1:catch 里 return 与 throw 区别?
A:return 后续 then 成功;throw 交给下一 catch。
总结
高频面试题速查
| 题 | 要点 |
|---|---|
| 回调地狱 | Promise 链 + return |
| executor | 同步执行,resolve 一次 |
| then 返回值 | 普通值 / Promise / throw |
| catch | 穿透 + 可恢复链 |
| ajaxPromise | XHR 包 Promise |
| Promise.resolve | 四种参数 |
| 微任务 | then 先于 setTimeout |
#mermaid-svg-xf2VVMHuHZROnbY1{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-xf2VVMHuHZROnbY1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-xf2VVMHuHZROnbY1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-xf2VVMHuHZROnbY1 .error-icon{fill:#552222;}#mermaid-svg-xf2VVMHuHZROnbY1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-xf2VVMHuHZROnbY1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-xf2VVMHuHZROnbY1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-xf2VVMHuHZROnbY1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-xf2VVMHuHZROnbY1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-xf2VVMHuHZROnbY1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-xf2VVMHuHZROnbY1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-xf2VVMHuHZROnbY1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-xf2VVMHuHZROnbY1 .marker.cross{stroke:#333333;}#mermaid-svg-xf2VVMHuHZROnbY1 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-xf2VVMHuHZROnbY1 p{margin:0;}#mermaid-svg-xf2VVMHuHZROnbY1 .edge{stroke-width:3;}#mermaid-svg-xf2VVMHuHZROnbY1 .section--1 rect,#mermaid-svg-xf2VVMHuHZROnbY1 .section--1 path,#mermaid-svg-xf2VVMHuHZROnbY1 .section--1 circle,#mermaid-svg-xf2VVMHuHZROnbY1 .section--1 polygon,#mermaid-svg-xf2VVMHuHZROnbY1 .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-xf2VVMHuHZROnbY1 .section--1 text{fill:#ffffff;}#mermaid-svg-xf2VVMHuHZROnbY1 .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-xf2VVMHuHZROnbY1 .edge-depth--1{stroke-width:17;}#mermaid-svg-xf2VVMHuHZROnbY1 .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-xf2VVMHuHZROnbY1 .disabled,#mermaid-svg-xf2VVMHuHZROnbY1 .disabled circle,#mermaid-svg-xf2VVMHuHZROnbY1 .disabled text{fill:lightgray;}#mermaid-svg-xf2VVMHuHZROnbY1 .disabled text{fill:#efefef;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-0 rect,#mermaid-svg-xf2VVMHuHZROnbY1 .section-0 path,#mermaid-svg-xf2VVMHuHZROnbY1 .section-0 circle,#mermaid-svg-xf2VVMHuHZROnbY1 .section-0 polygon,#mermaid-svg-xf2VVMHuHZROnbY1 .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-xf2VVMHuHZROnbY1 .section-0 text{fill:black;}#mermaid-svg-xf2VVMHuHZROnbY1 .node-icon-0{font-size:40px;color:black;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-xf2VVMHuHZROnbY1 .edge-depth-0{stroke-width:14;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-xf2VVMHuHZROnbY1 .disabled,#mermaid-svg-xf2VVMHuHZROnbY1 .disabled circle,#mermaid-svg-xf2VVMHuHZROnbY1 .disabled text{fill:lightgray;}#mermaid-svg-xf2VVMHuHZROnbY1 .disabled text{fill:#efefef;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-1 rect,#mermaid-svg-xf2VVMHuHZROnbY1 .section-1 path,#mermaid-svg-xf2VVMHuHZROnbY1 .section-1 circle,#mermaid-svg-xf2VVMHuHZROnbY1 .section-1 polygon,#mermaid-svg-xf2VVMHuHZROnbY1 .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-xf2VVMHuHZROnbY1 .section-1 text{fill:black;}#mermaid-svg-xf2VVMHuHZROnbY1 .node-icon-1{font-size:40px;color:black;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-xf2VVMHuHZROnbY1 .edge-depth-1{stroke-width:11;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-xf2VVMHuHZROnbY1 .disabled,#mermaid-svg-xf2VVMHuHZROnbY1 .disabled circle,#mermaid-svg-xf2VVMHuHZROnbY1 .disabled text{fill:lightgray;}#mermaid-svg-xf2VVMHuHZROnbY1 .disabled text{fill:#efefef;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-2 rect,#mermaid-svg-xf2VVMHuHZROnbY1 .section-2 path,#mermaid-svg-xf2VVMHuHZROnbY1 .section-2 circle,#mermaid-svg-xf2VVMHuHZROnbY1 .section-2 polygon,#mermaid-svg-xf2VVMHuHZROnbY1 .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-xf2VVMHuHZROnbY1 .section-2 text{fill:#ffffff;}#mermaid-svg-xf2VVMHuHZROnbY1 .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-xf2VVMHuHZROnbY1 .edge-depth-2{stroke-width:8;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-xf2VVMHuHZROnbY1 .disabled,#mermaid-svg-xf2VVMHuHZROnbY1 .disabled circle,#mermaid-svg-xf2VVMHuHZROnbY1 .disabled text{fill:lightgray;}#mermaid-svg-xf2VVMHuHZROnbY1 .disabled text{fill:#efefef;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-3 rect,#mermaid-svg-xf2VVMHuHZROnbY1 .section-3 path,#mermaid-svg-xf2VVMHuHZROnbY1 .section-3 circle,#mermaid-svg-xf2VVMHuHZROnbY1 .section-3 polygon,#mermaid-svg-xf2VVMHuHZROnbY1 .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-xf2VVMHuHZROnbY1 .section-3 text{fill:black;}#mermaid-svg-xf2VVMHuHZROnbY1 .node-icon-3{font-size:40px;color:black;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-xf2VVMHuHZROnbY1 .edge-depth-3{stroke-width:5;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-xf2VVMHuHZROnbY1 .disabled,#mermaid-svg-xf2VVMHuHZROnbY1 .disabled circle,#mermaid-svg-xf2VVMHuHZROnbY1 .disabled text{fill:lightgray;}#mermaid-svg-xf2VVMHuHZROnbY1 .disabled text{fill:#efefef;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-4 rect,#mermaid-svg-xf2VVMHuHZROnbY1 .section-4 path,#mermaid-svg-xf2VVMHuHZROnbY1 .section-4 circle,#mermaid-svg-xf2VVMHuHZROnbY1 .section-4 polygon,#mermaid-svg-xf2VVMHuHZROnbY1 .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-xf2VVMHuHZROnbY1 .section-4 text{fill:black;}#mermaid-svg-xf2VVMHuHZROnbY1 .node-icon-4{font-size:40px;color:black;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-xf2VVMHuHZROnbY1 .edge-depth-4{stroke-width:2;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-xf2VVMHuHZROnbY1 .disabled,#mermaid-svg-xf2VVMHuHZROnbY1 .disabled circle,#mermaid-svg-xf2VVMHuHZROnbY1 .disabled text{fill:lightgray;}#mermaid-svg-xf2VVMHuHZROnbY1 .disabled text{fill:#efefef;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-5 rect,#mermaid-svg-xf2VVMHuHZROnbY1 .section-5 path,#mermaid-svg-xf2VVMHuHZROnbY1 .section-5 circle,#mermaid-svg-xf2VVMHuHZROnbY1 .section-5 polygon,#mermaid-svg-xf2VVMHuHZROnbY1 .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-xf2VVMHuHZROnbY1 .section-5 text{fill:black;}#mermaid-svg-xf2VVMHuHZROnbY1 .node-icon-5{font-size:40px;color:black;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-xf2VVMHuHZROnbY1 .edge-depth-5{stroke-width:-1;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-xf2VVMHuHZROnbY1 .disabled,#mermaid-svg-xf2VVMHuHZROnbY1 .disabled circle,#mermaid-svg-xf2VVMHuHZROnbY1 .disabled text{fill:lightgray;}#mermaid-svg-xf2VVMHuHZROnbY1 .disabled text{fill:#efefef;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-6 rect,#mermaid-svg-xf2VVMHuHZROnbY1 .section-6 path,#mermaid-svg-xf2VVMHuHZROnbY1 .section-6 circle,#mermaid-svg-xf2VVMHuHZROnbY1 .section-6 polygon,#mermaid-svg-xf2VVMHuHZROnbY1 .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-xf2VVMHuHZROnbY1 .section-6 text{fill:black;}#mermaid-svg-xf2VVMHuHZROnbY1 .node-icon-6{font-size:40px;color:black;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-xf2VVMHuHZROnbY1 .edge-depth-6{stroke-width:-4;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-xf2VVMHuHZROnbY1 .disabled,#mermaid-svg-xf2VVMHuHZROnbY1 .disabled circle,#mermaid-svg-xf2VVMHuHZROnbY1 .disabled text{fill:lightgray;}#mermaid-svg-xf2VVMHuHZROnbY1 .disabled text{fill:#efefef;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-7 rect,#mermaid-svg-xf2VVMHuHZROnbY1 .section-7 path,#mermaid-svg-xf2VVMHuHZROnbY1 .section-7 circle,#mermaid-svg-xf2VVMHuHZROnbY1 .section-7 polygon,#mermaid-svg-xf2VVMHuHZROnbY1 .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-xf2VVMHuHZROnbY1 .section-7 text{fill:black;}#mermaid-svg-xf2VVMHuHZROnbY1 .node-icon-7{font-size:40px;color:black;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-xf2VVMHuHZROnbY1 .edge-depth-7{stroke-width:-7;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-xf2VVMHuHZROnbY1 .disabled,#mermaid-svg-xf2VVMHuHZROnbY1 .disabled circle,#mermaid-svg-xf2VVMHuHZROnbY1 .disabled text{fill:lightgray;}#mermaid-svg-xf2VVMHuHZROnbY1 .disabled text{fill:#efefef;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-8 rect,#mermaid-svg-xf2VVMHuHZROnbY1 .section-8 path,#mermaid-svg-xf2VVMHuHZROnbY1 .section-8 circle,#mermaid-svg-xf2VVMHuHZROnbY1 .section-8 polygon,#mermaid-svg-xf2VVMHuHZROnbY1 .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-xf2VVMHuHZROnbY1 .section-8 text{fill:black;}#mermaid-svg-xf2VVMHuHZROnbY1 .node-icon-8{font-size:40px;color:black;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-xf2VVMHuHZROnbY1 .edge-depth-8{stroke-width:-10;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-xf2VVMHuHZROnbY1 .disabled,#mermaid-svg-xf2VVMHuHZROnbY1 .disabled circle,#mermaid-svg-xf2VVMHuHZROnbY1 .disabled text{fill:lightgray;}#mermaid-svg-xf2VVMHuHZROnbY1 .disabled text{fill:#efefef;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-9 rect,#mermaid-svg-xf2VVMHuHZROnbY1 .section-9 path,#mermaid-svg-xf2VVMHuHZROnbY1 .section-9 circle,#mermaid-svg-xf2VVMHuHZROnbY1 .section-9 polygon,#mermaid-svg-xf2VVMHuHZROnbY1 .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-xf2VVMHuHZROnbY1 .section-9 text{fill:black;}#mermaid-svg-xf2VVMHuHZROnbY1 .node-icon-9{font-size:40px;color:black;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-xf2VVMHuHZROnbY1 .edge-depth-9{stroke-width:-13;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-xf2VVMHuHZROnbY1 .disabled,#mermaid-svg-xf2VVMHuHZROnbY1 .disabled circle,#mermaid-svg-xf2VVMHuHZROnbY1 .disabled text{fill:lightgray;}#mermaid-svg-xf2VVMHuHZROnbY1 .disabled text{fill:#efefef;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-10 rect,#mermaid-svg-xf2VVMHuHZROnbY1 .section-10 path,#mermaid-svg-xf2VVMHuHZROnbY1 .section-10 circle,#mermaid-svg-xf2VVMHuHZROnbY1 .section-10 polygon,#mermaid-svg-xf2VVMHuHZROnbY1 .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-xf2VVMHuHZROnbY1 .section-10 text{fill:black;}#mermaid-svg-xf2VVMHuHZROnbY1 .node-icon-10{font-size:40px;color:black;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-xf2VVMHuHZROnbY1 .edge-depth-10{stroke-width:-16;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-xf2VVMHuHZROnbY1 .disabled,#mermaid-svg-xf2VVMHuHZROnbY1 .disabled circle,#mermaid-svg-xf2VVMHuHZROnbY1 .disabled text{fill:lightgray;}#mermaid-svg-xf2VVMHuHZROnbY1 .disabled text{fill:#efefef;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-root rect,#mermaid-svg-xf2VVMHuHZROnbY1 .section-root path,#mermaid-svg-xf2VVMHuHZROnbY1 .section-root circle,#mermaid-svg-xf2VVMHuHZROnbY1 .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-xf2VVMHuHZROnbY1 .section-root text{fill:#ffffff;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-root span{color:#ffffff;}#mermaid-svg-xf2VVMHuHZROnbY1 .section-2 span{color:#ffffff;}#mermaid-svg-xf2VVMHuHZROnbY1 .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-xf2VVMHuHZROnbY1 .edge{fill:none;}#mermaid-svg-xf2VVMHuHZROnbY1 .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-xf2VVMHuHZROnbY1 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 本篇 Promise
基础
executor 同步
pending 三态
状态不可逆
实例方法
then 返回值规则
catch 穿透
finally 收尾
链式
Ajax 扁平化
Node fs 链
静态
Promise.resolve
Promise.reject
衔接
Ajax 进阶篇 ajax
async/await 进阶 async
【代码注释】
- 概括 本篇 知识树与练习顺序。
- executor 同步、then 微任务、链式 return 是三条主线。
- 衔接 Ajax 进阶篇 ajax 与 async/await 进阶 async/await。
- 配套 HTML/JS 按表 0.1 逐项练习。
知识主线:
- 异步痛点:回调地狱 → Promise 链 + 统一 catch。
- 状态机:pending → fulfilled / rejected,只变一次。
- then:返回新 Promise;返回 Promise 则「跟随」其状态。
- 实战 :
ajax-promise.js、定时器 Promise、Nodefs.promises顺序读文件。 - 静态方法 :
Promise.resolve/Promise.reject四种参数情形。 - 下一步:async/await 进阶 async/await、Promise.all/race。
建议练习顺序 :见 §0.4 --- 01 → 02 → 03(含 §6.4.1 作业 )→ 04 → 05。
相关资源: