Promise
Promise 是表示未来结果的对象。
[!info] related notes
- 所属 MOC: ecmascript异步, ecmascript-promises-and-async, ES6 新特性 MOC
- 前置概念: js事件循环
- 深入阅读: promise链, async / await, 异步错误处理
- 面试问法: Event Loop、宏任务和微任务怎么理解
- 关系笔记: ecmascript异步, ecmascript-promises-and-async
Promise
Promise 是为了解决“异步结果未来才会到”这个问题的。
你可以先记住一句:
Promise 是一个表示“未来结果”的对象。
为什么需要 Promise
最早很多异步代码这样写:
setTimeout(() => {
console.log("step1");
setTimeout(() => {
console.log("step2");
setTimeout(() => {
console.log("step3");
}, 1000);
}, 1000);
}, 1000);
这会形成层层嵌套,难读难维护。
Promise 的核心价值是:
- 把“未来会完成的结果”包装起来
- 让异步流程变得可链式组织
- 让成功 / 失败处理更统一
Promise 的三种状态
一个 Promise 有三种状态:
1. pending
进行中。
2. fulfilled
已成功。
3. rejected
已失败。
并且状态一旦从 pending 变成成功或失败,就不会再变。
最基础的 Promise 写法
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("done");
}, 1000);
});
这里:
new Promise(...)创建了一个 Promise- 传进去的函数会立即执行
resolve("done")表示成功reject(err)表示失败
then
p.then(onFulfilled, onRejected);
当 Promise 成功时,then 里的回调会执行。
[!info] 注意 then 中的参数应该是函数,非函数会被忽略 3. Promise then callbacks | BFE.dev - 前端刷题,准备前端面试拿到心仪的Offer。
then()里 不 return 会把后面的值变成undefinedreturn Promise.resolve(…).then(…)会等待内部 Promise 执行完Promise.reject(…)会跳到catchfinally()拿不到前面的结果finally()的返回值通常 不会改掉链条的值 4. Promise then callbacks II | BFE.dev - 前端刷题,准备前端面试拿到心仪的Offer。
一个完整例子
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("success");
}, 1000);
});
p.then((value) => {
console.log(value);
});
1 秒后输出:
success
Promise 不是“把代码变同步”
这一点很重要。
很多人刚学 Promise,会误以为:
有了 Promise,我就能马上拿到结果了。
不是。
比如:
const p = new Promise((resolve) => {
setTimeout(() => {
resolve(123);
}, 1000);
});
console.log(p);
这里立刻打印出来的不是 123,而是一个还没完成或已完成的 Promise 对象。
说明 Promise 不是同步结果,而是结果的占位符。
then 返回新的 Promise
这是 Promise 链式调用的根本原因。
Promise.resolve(1)
.then((value) => {
console.log(value);
return value + 1;
})
.then((value) => {
console.log(value);
return value + 1;
})
.then((value) => {
console.log(value);
});
输出:
1
2
3
每个 then 都会返回一个新的 Promise,所以可以继续接下去。
catch
Promise.reject("error")
.catch((err) => {
console.log(err);
});
用于处理失败。
finally
Promise.resolve("ok")
.finally(() => {
console.log("finally");
});
无论成功失败都会执行,适合做收尾动作。
Promise 回调为什么常常比 setTimeout 早
因为 .then(...) 的回调属于微任务,而 setTimeout 属于宏任务。
setTimeout(() => console.log("timeout"), 0);
Promise.resolve().then(() => {
console.log("promise");
});
输出:
promise
timeout
Promise 解析过程(Promise Resolution Procedure)
这是 Promise 最容易被忽略的深层机制:resolve 一个 Promise / thenable 时到底发生了什么?
核心规则:状态可以跟随,身份不会复用
当 resolve(x) 中的 x 本身是一个 Promise 或 thenable(有 .then 方法的对象)时,新 Promise 不会把 x 当普通值包一层,而是采用它的状态和值。
但它们仍然是不同的对象。
Promise.resolve(x) 的特殊行为
const p1 = Promise.resolve(1)
const p3 = Promise.resolve(p1)
p1 === p3 // true
如果参数已经是 Promise,Promise.resolve 直接返回它本身。 这是唯一会”复用同一个对象”的情况。
对比:
const p1 = Promise.resolve(1)
const p2 = new Promise((resolve) => resolve(p1))
p1 === p2 // false
new Promise 一定会创建新对象。resolve(p1) 只会让 p2 跟随 p1 的状态,但 p2 是独立的新对象。
then() 永远返回新 Promise
const p1 = Promise.resolve(1)
const p4 = p1.then(() => p1)
p1 === p4 // false
即使回调返回 p1 本身,then 返回的 p4 也是新的 Promise 对象。p4 的状态和值会跟随 p1,但身份不同。
完整身份判断示例
const p1 = Promise.resolve(1)
const p2 = new Promise((resolve) => resolve(p1))
const p3 = Promise.resolve(p1)
const p4 = p2.then(() => new Promise((resolve) => resolve(p3)))
const p5 = p4.then(() => p4)
console.log(p1 == p2) // false new Promise 总是新建
console.log(p1 == p3) // true Promise.resolve(已有Promise) 直接返回原 Promise
console.log(p3 == p4) // false then 总是返回新 Promise
console.log(p4 == p5) // false then 总是返回新 Promise
解析规则总结
| 情况 | 是否新建对象 | 状态如何 |
|---|---|---|
new Promise(resolve => resolve(x)),x 是普通值 | 新建 | 直接 fulfilled |
new Promise(resolve => resolve(p)),p 是 Promise | 新建 | 跟随 p |
Promise.resolve(p),p 是 Promise | 不新建,返回 p 本身 | p 的状态 |
Promise.resolve(x),x 是普通值 | 新建 | fulfilled |
p.then(fn),fn 返回普通值 | 新建 | fulfilled,值为返回值 |
p.then(fn),fn 返回 Promise | 新建 | 跟随返回的 Promise |
p.then(fn),fn 返回 thenable | 新建 | 跟随 thenable(调用其 .then) |
thenable 同化(thenable assimilation)
如果 resolve(x) 中的 x 是一个有 .then 方法的对象(但不是真正的 Promise),引擎会调用 x.then 来获取最终状态:
const thenable = {
then(resolve) {
resolve(42);
}
};
const p = Promise.resolve(thenable);
p.then(v => console.log(v)); // 42
这就是 Promise Resolution Procedure 的核心:遇到 thenable 就展开,直到拿到非 thenable 的值。
Promise 链的本质
你可以把它理解成:
前一个异步步骤完成后,把结果交给下一个步骤。
例如:
function step1() {
return Promise.resolve(1);
}
function step2(value) {
return Promise.resolve(value + 1);
}
function step3(value) {
return Promise.resolve(value + 1);
}
step1()
.then(step2)
.then(step3)
.then((result) => {
console.log(result); // 3
});
和其他笔记怎么分工
- 学
then/catch/finally如何串成流程:promise链 - 学更接近同步代码的写法:async / await
- 学输出顺序题:js事件循环