Promise 链式调用深度解析
📚 目录
📌 Promise1 源码实现
完整的 Promise1 实现代码:
typescript
class Promise1 {
private status: string = 'pending'
private value: any
private reason: any
private callbacks: any[] = []
constructor(executor: (resolve: (value: any) => void, reject: (reason: any) => void) => void) {
const resolve = (value: any) => {
if (this.status !== 'pending') return
this.status = 'fulfilled'
this.value = value
this.callbacks.forEach(cb => {
queueMicrotask(() => {
cb.onFulfilled(this.value)
})
})
}
const reject = (reason: any) => {
if (this.status !== 'pending') return
this.status = 'rejected'
this.reason = reason
this.callbacks.forEach(cb => {
queueMicrotask(() => {
cb.onRejected(this.reason)
})
})
}
try {
executor(resolve, reject) // 把resolve的控制权交给用户
} catch (error) {
reject(error)
}
}
then(onFulfilled: (value: any) => any, onRejected: (reason: any) => void) {
return new Promise1((resolve: (value: any) => void, reject: (reason: any) => void): any => {
const handleCallback = (callback: (value: any) => any, value: any) => {
try {
const result = callback(value)
if (result instanceof Promise1) {
result.then(resolve, reject)
} else {
resolve(result)
}
}
catch (error) {
reject(error)
}
}
if (this.status === 'pending') {
this.callbacks.push({
onFulfilled: (value: any) => handleCallback(onFulfilled, value),
onRejected: (reason: any) => handleCallback(onRejected, reason)
})
}
else if (this.status === 'fulfilled') {
queueMicrotask(() => {
handleCallback(onFulfilled, this.value)
})
}
else if (this.status === 'rejected') {
queueMicrotask(() => {
onRejected(this.reason)
})
}
})
}
}
代码关键点注解:
| 行号 | 代码 | 说明 |
|---|---|---|
| 2-5 | private status/value/reason/callbacks |
Promise 的私有状态 |
| 7-16 | const resolve = (value) => {...} |
resolve 函数(箭头函数捕获 this) |
| 9 | this.status = 'fulfilled' |
修改状态为已完成 |
| 10 | this.value = value |
保存 resolve 的值 |
| 11-15 | this.callbacks.forEach(...) |
触发所有已注册的回调 |
| 12 | queueMicrotask(...) |
异步执行回调(微任务) |
| 28 | executor(resolve, reject) |
执行用户传入的 executor |
| 35 | const handleCallback = ... |
核心:处理回调、捕获 resolve/reject |
| 38-39 | if (result instanceof Promise1) |
Promise 扁平化:检查返回值 |
| 39 | result.then(resolve, reject) |
关键:把外层的 resolve/reject 传给内层 |
| 49-53 | if (this.status === 'pending') |
pending 状态:注册回调 |
| 55-59 | else if (this.status === 'fulfilled') |
fulfilled 状态:立即执行(异步) |
核心示例
使用 Promise1 实现链式调用:
javascript
// 创建 p1
const p1 = new Promise1((resolve) => resolve(10));
// p1.then 返回 p2
const p2 = p1.then((v1) => {
// 返回一个 Promise (innerPromise)
return new Promise1((resolve) => resolve(100));
});
// p2.then 返回 p3
const p3 = p2.then((v2) => {
console.log(v2); // 输出: 100
});
核心机制 :当 .then() 回调返回一个 Promise 时,会执行:
javascript
innerPromise.then(resolve_p2, reject_p2);
// ^^^^^^^^^ ^^^^^^^^^
// 把外层 Promise 的 resolve/reject 作为内层 Promise 的回调
💭 个人总结(完整执行流程概述)
主要流程
首先 p1 执行 executor,即 resolve => resolve(10),然后修改 p1.status = 'fulfilled',发现此时回调还未注册,继续执行。
调用 p1.then 注册 p1 的回调并返回 p2 。当 p2 的回调注册时,由于调用的是 p1.then,所以 this 指向 p1。在 then 执行时发现 this.status(即 p1 的状态)是 fulfilled,然后立刻执行回调(异步),加入微任务队列。
继续执行注册 p2 的回调,返回 p3。
微任务执行
现在主线程空闲,执行微任务(p1 的回调):
javascript
handleCallback(onFulfilled, this.value);
// this 指向 p1,所以 this.value = 10
onFulfilled 是:
javascript
(v1) => {
return new Promise1((resolve) => resolve(100));
};
所以变成了:
javascript
handleCallback((v1) => {
return new Promise1((resolve) => resolve(100));
}, 10);
在 handleCallback 中执行:
javascript
10 => {
return new Promise1(resolve => resolve(100));
}
注意 :这里 10 不会被传递下去,返回了一个 Promise,现在统称为 innerPromise。
Promise 扁平化的关键
由于 handleCallback 定义在 p2 中,所以它通过闭包捕获了 p2 的 resolve 和 reject 的引用(现在称为 resolve_p2 和 reject_p2)。
发现 result 是一个 innerPromise,所以执行:
javascript
innerPromise.then(resolve_p2, reject_p2);
传入 resolve_p2 和 reject_p2 作为 innerPromise 的 onFulfilled 和 onRejected。
innerPromise 的执行
执行 return new innerPromise(executor),这里的 executor 就是 resolve => resolve(100)。
执行 executor,但这里的 resolve 和 reject 是 innerResolve 和 innerReject。然后执行把 100 保存到 innerPromise.value 上,修改 innerPromise.status = 'fulfilled',执行 innerCallbacks(但此时还不存在)。
接着调用 innerPromise.then(resolve_p2, reject_p2),返回一个 innerInnerPromise,然后把 resolve_p2 和 reject_p2 通过 handleCallback 包装一次。
由于是 innerPromise.then,所以此时 this 指向 innerPromise,innerPromise.status 是 fulfilled,然后执行回调:
javascript
queueMicrotask(() => {
handleCallback(onFulfilled, this.value);
});
神奇之处:两个 this
这里就是设计的神奇之处!
onFulfilled 是 resolve_p2,resolve_p2 是一个箭头函数,它内部的 this 指向定义时的上下文(p2)。
执行回调时:
javascript
handleCallback(onFulfilled, this.value);
// ^^^^^^^^^^^ ^^^^^^^^^^
// resolve_p2 innerPromise.value
注意有两个 this:
- 参数里的
this是innerPromise的 this,所以this.value = 100 resolve_p2内部的this是 p2,所以执行resolve_p2(100)时会把值100赋给p2.value
Promise 扁平化完成 :通过 p2 的 resolve 把看似内部的链条又重新链接回了外部!
最后阶段
现在 p2 的 resolve 执行了,p2.status = 'fulfilled',所以执行 p2 的回调:
javascript
(v2) => {
console.log(v2);
};
这个回调注册在 p2.callbacks 上,通过遍历数组得到 onFulfilled,然后:
javascript
cb.onFulfilled(this.value);
// this 指向 p2(因为是 p2 的 resolve 触发的,别以为是 p3!)
// this.value = 100
输出 100,完成整个链条!
🚨 常见错误
我之前犯过的错误:
如果最后一步 .then 是:
javascript
.then(v2 => {
console.log(v2);
resolve(111) // ❌ 错误!
})
这是同样的错误 :试图在外部定义中获取 resolve,但它实际上并不存在!
问题本质:
v2 => { console.log(v2); resolve(111) } 是作为 then 的参数 onFulfilled 传入的,但这个 resolve:
- 既不存在于 p2 :p2 已经执行完了,p2 的
resolve在外部获取不到 - 也不存在于 p3:虽然这是 p2 的回调,但运行在 p3 的环境中
- 作用域是独立的:它的执行环境独立于 p2 和 p3
类比:
javascript
function fn() {
console.log(value); // ❌ 找不到 value
}
function fn1(value) {
fn(); // 在 fn1 中调用,但 fn 访问不到 fn1 的 value
}
正确做法:返回新的 Promise,通过参数获取 resolve:
javascript
.then(v2 => {
console.log(v2);
return new Promise1(resolve => {
resolve(111) // ✅ 通过参数获取
})
})
完整执行流程
阶段 1:同步执行阶段
步骤 1:创建 p1
javascript
const p1 = new Promise1((resolve) => resolve(10));
执行过程:
- 执行 executor:
resolve => resolve(10) - 调用
resolve(10) - 修改
p1.status = 'fulfilled' - 保存
p1.value = 10 - 遍历
p1.callbacks(此时为空,没有回调需要触发)
状态:
p1.status = 'fulfilled'p1.value = 10p1.callbacks = []
步骤 2:调用 p1.then,创建 p2
javascript
const p2 = p1.then((v1) => {
return new Promise1((resolve) => resolve(100));
});
执行过程:
-
调用
p1.then(onFulfilled)this指向p1(因为是p1.then)onFulfilled = v1 => { return new Promise1(...) }
-
检查
this.status(即p1.status)- 发现是
'fulfilled' - 进入分支:
javascriptelse if (this.status === 'fulfilled') { queueMicrotask(() => { handleCallback(onFulfilled, this.value) }) } - 发现是
-
将回调加入微任务队列
- 微任务队列 :
[任务A: handleCallback(onFulfilled, 10)]
- 微任务队列 :
-
返回
p2(pending 状态)
状态:
p2.status = 'pending'p2.value = undefined- 微任务队列:
[任务A]
步骤 3:调用 p2.then,创建 p3
javascript
const p3 = p2.then((v2) => {
console.log(v2);
});
执行过程:
-
调用
p2.then(onFulfilled)this指向p2onFulfilled = v2 => console.log(v2)
-
检查
this.status(即p2.status)- 发现是
'pending' - 进入分支:
javascriptif (this.status === "pending") { this.callbacks.push({ onFulfilled: (value) => handleCallback(onFulfilled, value), onRejected: (reason) => handleCallback(onRejected, reason), }); } - 发现是
-
注册回调到
p2.callbacks
状态:
p3.status = 'pending'p2.callbacks = [{ onFulfilled: ... }]- 微任务队列:
[任务A]
同步阶段结束,进入微任务阶段。
阶段 2:微任务阶段 1 - 执行 p1 的回调
任务 A:handleCallback(onFulfilled, 10)
javascript
handleCallback((v1) => {
return new Promise1((resolve) => resolve(100));
}, 10);
执行过程:
-
执行回调:
javascriptconst result = onFulfilled(10); // = (v1 => { return new Promise1(...) })(10) -
创建 innerPromise:
javascriptconst innerPromise = new Promise1((resolve) => resolve(100));- 执行 executor:
resolve => resolve(100) - 调用
resolve(100)(这是 innerPromise 的 resolve) innerPromise.status = 'fulfilled'innerPromise.value = 100innerPromise.callbacks = [](空)
- 执行 executor:
-
检查返回值:
javascriptif (result instanceof Promise1) { // result = innerPromise result.then(resolve, reject); // ^^^^^^^ ^^^^^^ // p2 的 resolve 和 reject }关键设计点:
handleCallback定义在 p2 的构造器中- 通过闭包捕获了
resolve_p2和reject_p2 - 现在将它们作为回调传递给 innerPromise
-
执行 innerPromise.then(resolve_p2, reject_p2):
创建 innerInnerPromise:
javascriptconst innerInnerPromise = innerPromise.then(resolve_p2, reject_p2);内部执行:
this指向innerPromisethis.status = 'fulfilled'- 进入分支:
javascriptelse if (this.status === 'fulfilled') { queueMicrotask(() => { handleCallback(onFulfilled, this.value) // ^^^^^^^^^^^ ^^^^^^^^^^ // resolve_p2 innerPromise.value = 100 }) }- 将新任务加入微任务队列
- 微任务队列 :
[任务B: handleCallback(resolve_p2, 100)]
状态:
innerPromise.status = 'fulfilled'innerPromise.value = 100- 微任务队列:
[任务B]
阶段 3:微任务阶段 2 - 执行 innerPromise 的回调
任务 B:handleCallback(resolve_p2, 100)
javascript
handleCallback(resolve_p2, 100);
神奇之处 :这里有两个 this!
执行过程:
-
调用参数中的
this.value:javascripthandleCallback(onFulfilled, this.value); // ^^^^^^^^^^ // 这里的 this = innerPromise // this.value = 100 -
执行 onFulfilled(即 resolve_p2):
javascriptconst result = onFulfilled(100); // = resolve_p2(100) -
resolve_p2 内部的 this:
javascriptconst resolve_p2 = (value) => { // 这是箭头函数,this 指向定义时的上下文 // 定义时在 p2 的构造器中 // 所以 this = p2 if (this.status !== "pending") return; this.status = "fulfilled"; // p2.status = 'fulfilled' this.value = value; // p2.value = 100 // 触发 p2 的回调 this.callbacks.forEach((cb) => { queueMicrotask(() => { cb.onFulfilled(this.value); }); }); }; -
p2 的状态改变:
p2.status = 'fulfilled'p2.value = 100✅ 值从 innerPromise 传递到 p2!- 将 p2 的回调加入微任务队列
- 微任务队列 :
[任务C: p2 的回调]
关键理解:
handleCallback(onFulfilled, this.value)中:- 参数中的
this=innerPromise(调用环境) onFulfilled内部的this=p2(定义环境,箭头函数)
- 参数中的
- Promise 扁平化 :通过
resolve_p2(100)将内层 Promise 的值传递给外层 Promise
阶段 4:微任务阶段 3 - 执行 p2 的回调
任务 C:p2 的回调
javascript
cb.onFulfilled(this.value);
// this = p2(因为是 p2.resolve 触发的)
// this.value = 100
执行过程:
-
调用回调:
javascriptcb.onFulfilled(100); // = (value => handleCallback(v2 => console.log(v2), value))(100) -
执行 handleCallback:
javascripthandleCallback((v2) => console.log(v2), 100); -
执行用户回调:
javascriptconst result = ((v2) => console.log(v2))(100); // 输出: 100 // result = undefined -
resolve p3:
javascriptif (result instanceof Promise1) { // 否 } else { resolve(result); // resolve_p3(undefined) }p3.status = 'fulfilled'p3.value = undefined
注意:
cb.onFulfilled(this.value)中的this指向p2,不是 p3!- 因为这个回调是 p2 的 resolve 触发的
this指向触发者(p2),不是回调注册者(p3)
执行完毕!链条结束。
关键设计点
1. 闭包捕获 resolve
javascript
// p2 的构造器中
return new Promise1((resolve_p2, reject_p2) => {
const handleCallback = (callback, value) => {
// handleCallback 通过闭包捕获了 resolve_p2 和 reject_p2
const result = callback(value);
if (result instanceof Promise1) {
result.then(resolve_p2, reject_p2);
// ^^^^^^^^^ ^^^^^^^^^
// 使用闭包捕获的 resolve_p2
}
};
});
作用:让 innerPromise 完成时能够改变 p2 的状态。
2. Promise 扁平化机制
javascript
innerPromise.then(resolve_p2, reject_p2);
流程:
scss
innerPromise resolve(100)
↓
触发回调 resolve_p2(100)
↓
p2.value = 100
↓
p2 的回调执行
效果:
- 用户写:
p2.then(v2 => console.log(v2)) - 得到:
100(不是 Promise 对象) - 自动扁平化!
3. 两个 this 的区别
javascript
handleCallback(onFulfilled, this.value);
// ^^^^^^^^^^^ ^^^^^^^^^^
// resolve_p2 innerPromise.value
// (内部 this = p2) (外部 this = innerPromise)
关键理解:
- 外部 this (调用环境):
innerPromise.then内部,this = innerPromise - 内部 this (定义环境):
resolve_p2是箭头函数,定义在 p2 构造器中,this = p2
4. 参数传递时机
javascript
// 注册回调时(定义)
this.callbacks.push({
onFulfilled: (value) => handleCallback(onFulfilled, value),
// ^^^^^
// 参数,还没有值
});
// resolve 执行时(调用)
cb.onFulfilled(this.value);
// ^^^^^^^^^^
// 传入参数,value 被赋值
关键:
- 参数在调用时赋值,不是定义时
value来自this.value(Promise 的值)
常见错误:作用域陷阱
错误示例
javascript
p2.then((v2) => {
console.log(v2);
resolve(111); // ❌ ReferenceError: resolve is not defined
});
为什么会报错?
问题本质
v2 => { console.log(v2); resolve(111) } 是作为 then 的参数 onFulfilled 传入的,但这个 resolve:
-
不存在于 p2
- p2 的 resolve 在 p2 的构造器中
- 构造器执行完毕后,resolve 就不在任何外部可访问的作用域中了
-
不存在于 p3
- p3 的 resolve 在 p3 的构造器中
- 但回调函数定义在外部,作用域链不包含 p3 的构造器
-
这是 p2 的回调
- 回调注册在
p2.callbacks中 - 但运行在 p3 的环境中(p3 的 handleCallback)
- 回调注册在
-
它的作用域是独立的
- 作用域链:
[callback] → [外部作用域] - 不包含 p2 或 p3 的作用域
- 作用域链:
作用域分析
javascript
// ===== 外部作用域 =====
const callback = (v2) => {
// 作用域链:[callback] → [外部]
console.log(v2);
resolve(111); // ❌ 沿着作用域链找,找不到
};
// ===== p2 的构造器(已销毁)=====
new Promise1((resolve_p2, reject_p2) => {
// resolve_p2 在这里
// 但构造器执行完就销毁了
});
// ===== p3 的构造器 =====
new Promise1((resolve_p3, reject_p3) => {
// resolve_p3 在这里
// 但 callback 的作用域链不包含这里
handleCallback(callback, value);
// callback 执行时访问不到 resolve_p3
});
类比:函数作用域
这个错误和以下情况完全相同:
javascript
function fn() {
console.log(value); // ❌ ReferenceError: value is not defined
}
function fn1(value) {
fn(); // 在 fn1 中调用,但 fn 访问不到 fn1 的 value
}
fn1(100);
核心原则:
- 函数的作用域链在定义时确定
- 调用位置不影响作用域链
- 即使在某个环境中调用,也访问不到该环境的变量
正确做法
如果想在异步操作后 resolve,需要返回新的 Promise:
javascript
p2.then((v2) => {
console.log(v2); // 100
// ✅ 返回新 Promise,通过参数获取 resolve
return new Promise1((resolve) => {
// ^^^^^^^ 参数!
setTimeout(() => {
resolve(111); // ✅ 可以使用
}, 1000);
});
});
总结
核心机制
- 状态判断:根据 Promise 的状态(pending/fulfilled/rejected)决定如何处理回调
- 闭包捕获:handleCallback 通过闭包捕获外层 Promise 的 resolve/reject
- Promise 扁平化 :
innerPromise.then(resolve_p2, reject_p2)实现值的传递 - 参数传递:回调的参数在 resolve 调用时赋值,不是定义时
关键理解
-
this 指向:
- 方法调用:
p1.then()中this = p1 - 箭头函数:捕获定义时的
this
- 方法调用:
-
作用域链:
- 函数的作用域链在定义时确定
- 调用位置不改变作用域链
- 外部定义的函数访问不到内部的变量
-
Promise 扁平化:
- 通过
result.then(resolve, reject)将内层 Promise 的 resolve/reject 设置为外层的 - 实现值的自动传递,用户无需手动处理嵌套
- 通过
设计哲学
Promise 的设计精妙之处在于:
- 通过闭包和参数传递实现状态同步
- 通过
.then(resolve, reject)实现 Promise 扁平化 - 通过箭头函数和作用域链保证
this指向正确 - 通过微任务队列保证异步执行顺序
这些机制共同构成了 Promise 强大而优雅的链式调用能力!
参考代码
完整的 Promise1 实现:
typescript
class Promise1 {
private status: string = "pending";
private value: any;
private reason: any;
private callbacks: any[] = [];
constructor(
executor: (
resolve: (value: any) => void,
reject: (reason: any) => void
) => void
) {
const resolve = (value: any) => {
if (this.status !== "pending") return;
this.status = "fulfilled";
this.value = value;
this.callbacks.forEach((cb) => {
queueMicrotask(() => {
cb.onFulfilled(this.value);
});
});
};
const reject = (reason: any) => {
if (this.status !== "pending") return;
this.status = "rejected";
this.reason = reason;
this.callbacks.forEach((cb) => {
queueMicrotask(() => {
cb.onRejected(this.reason);
});
});
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled: (value: any) => any, onRejected: (reason: any) => void) {
return new Promise1(
(resolve: (value: any) => void, reject: (reason: any) => void): any => {
const handleCallback = (callback: (value: any) => any, value: any) => {
try {
const result = callback(value);
if (result instanceof Promise1) {
result.then(resolve, reject);
} else {
resolve(result);
}
} catch (error) {
reject(error);
}
};
if (this.status === "pending") {
this.callbacks.push({
onFulfilled: (value: any) => handleCallback(onFulfilled, value),
onRejected: (reason: any) => handleCallback(onRejected, reason),
});
} else if (this.status === "fulfilled") {
queueMicrotask(() => {
handleCallback(onFulfilled, this.value);
});
} else if (this.status === "rejected") {
queueMicrotask(() => {
onRejected(this.reason);
});
}
}
);
}
}
这份文档总结了 Promise 链式调用的核心机制,包括执行流程、关键设计点和常见陷阱。希望能帮助你深入理解 Promise 的工作原理! 🎉