Promise核心原理深度解析
拆解 Promise 状态机机制、then 链式调用、错误冒泡与微任务调度,掌握异步编程的核心范式。
一句话概括
Promise 本质上是一个带有回调队列的状态机——pending 时囤积回调,fulfilled/rejected 后逐一唤醒;每个 then 又生出一个新 Promise,链式调用的根基全在于此。
核心知识点
1. 状态机:一次变心,终身不悔
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
constructor(executor) {
this.state = PENDING;
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.state !== PENDING) return; // 👈 不可逆
this.state = FULFILLED;
this.value = value;
this.onFulfilledCallbacks.forEach(fn => fn());
};
const reject = (reason) => {
if (this.state !== PENDING) return;
this.state = REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
};
try { executor(resolve, reject); } catch (e) { reject(e); }
}
}
三个关键:状态不可逆、回调队列、executor 异常自动 reject。
2. then 返回新 Promise —— 链的基石
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
then(onFulfilled, onRejected) {
// 值穿透:没传回调就原样传递
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
onRejected = typeof onRejected === 'function' ? onRejected : e => { throw e; };
return new MyPromise((resolve, reject) => {
const handle = (fn, value) => {
queueMicrotask(() => {
try {
const x = fn(value);
// 如果回调返回 Promise,等待它落定
if (x instanceof MyPromise) x.then(resolve, reject);
else resolve(x);
} catch (e) { reject(e); }
});
};
if (this.state === FULFILLED) handle(onFulfilled, this.value);
else if (this.state === REJECTED) handle(onRejected, this.reason);
else {
this.onFulfilledCallbacks.push(() => handle(onFulfilled, this.value));
this.onRejectedCallbacks.push(() => handle(onRejected, this.reason));
}
});
}
链式调用的灵魂:then 不返回 this,而是返回全新的 Promise。新 Promise 的状态由回调的返回值决定。
3. 错误冒泡 —— 没 catch 就一直漏下去
1
2
3
4
5
6
7
8
9
Promise.resolve()
.then(() => { throw new Error('boom'); }) // rejected
.then(() => console.log('skip')) // 被跳过
.catch(e => console.log(e.message)); // 接住了 boom
// catch 之后链恢复
Promise.reject('err')
.catch(() => 'recovered')
.then(v => console.log(v)); // 'recovered'
原理:then(onFulfilled) 等价于 then(onFulfilled, undefined),undefined 被替换成默认的 e => { throw e }——把错误往下传。
4. 微任务调度 —— queueMicrotask
1
2
3
4
console.log('start');
Promise.resolve().then(() => console.log('promise'));
console.log('end');
// 输出:start → end → promise
Promise 回调必须异步执行,且进微任务队列。这意味着它会抢在 setTimeout 前面,但排在同步代码后面。
5. resolve(Promise) 的特殊处理
1
2
const inner = new Promise(r => setTimeout(() => r('done'), 100));
new Promise(resolve => resolve(inner)).then(console.log); // 100ms 后输出 done
如果 resolve 收到另一个 Promise,会”等待”它完成——规范叫 Promise Resolution Procedure,目的是让嵌套 Promise 自动展平。
「其实你每天都在用」
1. fetch API 的返回值
1
fetch('/api/user').then(r => r.json()); // fetch 返回 Promise
2. 图片预加载
1
2
3
4
5
6
const loadImg = src => new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = reject;
img.src = src;
});
3. setTimeout 的 Promise 化
1
2
const sleep = ms => new Promise(r => setTimeout(r, ms));
await sleep(1000); // 等 1 秒
4. async/await 底层
每个 await 等价于一个 .then();try/catch 等价于 .catch()。你写的 async 函数本质上是 Promise 链。
5. 上传进度监听
1
2
3
4
5
6
7
new Promise((resolve) => {
const xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', e => updateBar(e.loaded / e.total));
xhr.onload = resolve;
xhr.open('POST', '/upload');
xhr.send(formData);
});
常见误解(FAQ)
❌ 误区 1:「Promise 构造函数是异步的」
Promise 的 executor(new Promise(fn) 里的 fn)是同步执行的。只有 .then() / .catch() / .finally() 里的回调是异步的。这是事件循环题的常见陷阱。
❌ 误区 2:「catch 之后不能再 then」
可以。catch 返回的也是一个 Promise,如果 catch 里没抛新错误,状态就是 fulfilled,后面的 then 正常执行。
❌ 误区 3:「finally 不改变链的值」
finally 的回调不接收值,也不改变 Promise 的最终结果——它会”透传”上一个 Promise 的值或错误。唯一的例外:如果 finally 回调本身抛异常或返回 rejected Promise,新错误会覆盖旧值。
❌ 误区 4:「resolve 一个 rejected Promise,当前 Promise 也 rejected」
对。resolve(Promise.reject('err')) 的结果是 rejected。因为 resolve 检测到参数是 Promise 后会”订阅”它的结果,而不是盲目地 fulfilled。
一句话总结
Promise 把”回调”变成了”值”——你可以把异步结果像普通变量一样传来传去、组合拼装,这彻底改变了 JavaScript 的编程模型。