Promise 是前端面试常考点,不管概念还是手写题都绕不开。
不知道你是不是跟我一样 ------ 每次手写完,过阵子不碰,下次又卡壳,妥妥的 "失忆式编程"。
我总在想:能不能像做数学题那样,记几个 "公式" 就顺着推?毕竟编程也有规律,抓住关键点就能推导实现原理。所以才有了这篇文。
:::warning 大致推理逻辑:从使用场景 → 总结 "数学公式"→ 按公式推实现逻辑
:::
文章大纲主要如下:
- Promise.resolve 的概念
- Promise.resolve 的使用场景
- 从场景总结出 "数学公式"
- 根据 "公式" 推导出实现逻辑
- "公式"概括总结
Promise.resolve 的概念
先搞懂最基础的:Promise.resolve
是 Promise 对象自带的静态方法 ------ 不用先 new 一个 Promise 实例,直接写 Promise.resolve(...)
就能用。
它的核心作用很简单:快速返回一个 "已成功(fulfilled)" 的 Promise 对象。不过有个小细节:如果传进去的参数本身就是 Promise 实例,它会直接把这个实例返回,不会多包一层 ------ 这点后面推导实现时会用到,先记个小重点就好。
简单说就是:你给它点 "原料",它要么给你包成成功的 Promise,要么直接把现成的 Promise 还给你,省了手动写 new Promise 的麻烦。
Promise.resolve 的使用场景
下面先来看下 Promise.resolve 使用的大多数具体场景,结合代码例子:
场景 1:处理基本数据类型 & 处理空值 & 处理无参数 & 处理引用数据类型
如果要把字符串、数字、布尔值、null、undefined 这些基本类型,快速变成 "已完成" 的 Promise,直接传进去就行,后续用 .then 就能拿到值:
javascript
// 数字
Promise.resolve(123).then((v) => console.log(v)); // 输出: 123
// 字符串
Promise.resolve("hello").then((v) => console.log(v)); // 输出: "hello"
// 布尔值
Promise.resolve(false).then((v) => console.log(v)); // 输出: false
// null
Promise.resolve(null).then((v) => console.log(v)); // 输出: null
// undefined
Promise.resolve(undefined).then((v) => console.log(v)); // 输出: undefined
当没传参数,它也能正常返回 Promise,.then
里能拿到就是 undefined
:
javascript
// 无参数
Promise.resolve().then((v) => console.log(v)); // 输出: undefined
遇到不含 then 方法的普通对象或者数组时,用 Promise.resolve
包一下,通过 .then
里就能直接拿到完整对象,不用额外处理:
javascript
// 对象
const obj = { a: 1, b: 2 };
Promise.resolve(obj).then((v) => console.log(v)); // 输出: { a: 1, b: 2 }
// 数组
const arr = [1, 2];
Promise.resolve(arr).then((v) => console.log(v)); // 输出: [1, 2]
场景 2:处理 Promise 实例
如果传进去的本身就是个 Promise 实例,它不会多包一层,而是直接返回这个实例 ------ 成功的就走.then
,失败的就走 .catch
:
javascript
// 成功的 Promise
const successP = Promise.resolve("ok");
Promise.resolve(successP).then((v) => console.log(v)); // 输出: "ok"
// 失败的 Promise
const failP = Promise.reject("error");
Promise.resolve(failP).catch((v) => console.log(v)); // 输出: "error"
// 失败的 Promise
let p1 = new Promise((resolve, reject) => {
reject(-200);
});
Promise.resolve(p1).then(
(data) => {
console.log(data);
},
(reason) => {
console.log(reason); // 输出:-200
}
);
场景 3:处理 thenable 对象
thenable 对象:是一种 "长得像 Promise,但不是正经 Promise" 的对象 ------ 它唯一的特点就是带一个 then
方法。
Promise.resolve
遇到这种对象时,会专门处理它的 then
方法,这里有两个关键逻辑要讲清楚:
- then 方法只认一个参数 :thenable 的
then
方法只需要接收一个参数,这个参数是resolve
回调(可以理解成 "成功时要执行的函数")。
:::warning 当 Promise.resolve
处理它时,会主动调用这个 then
方法,并且把自己的 resolve
传进去 ------ 只要在 then
里调用了这个 resolve
,后续 .then
就能拿到值。
:::
- 失败怎么处理?靠抛错 :因为 thenable 没有正经 Promise 的 reject 回调,所以如果想让它 "失败",只能在 then 方法内部主动抛错(用
throw
)。一旦抛错,Promise.resolve
就会把这个错误传给后续的.catch
。
看两个例子就懂了:
javascript
// 例子1:thenable 成功(调用 resolve)
const basicThenable = {
// then 只接收 resolve 一个参数
then: (resolve) => {
// 调用 resolve,把结果传出去
resolve("done");
},
};
// Promise.resolve 内部会执行 basicThenable.then 方法,所以后续 .then 能拿到 "done"
Promise.resolve(basicThenable).then((v) => console.log(v)); // 输出: "done"
// 例子2:thenable 失败(主动抛错)
const errorThenable = {
then: (resolve) => {
// 没调用 resolve,反而抛了个错
throw "boom";
// 这里就算不写 resolve,只要抛错,就会触发后续 .catch
},
};
// Promise.resolve 捕获到抛错,所以后续 .catch 能拿到 "boom"
Promise.resolve(errorThenable).catch((v) => console.log(v)); // 输出: "boom"
简单总结,处理 thenable 时,Promise.resolve
就干两件事:
- 调用 thenable 的 then 方法,并传递自身的 resolve 回调
- 捕获 thenable 的 then 方法内部抛错,交给自身的 reject 处理
从场景总结的"数学公式"
前面梳理了 Promise.resolve
的各种使用场景,其实这些场景背后藏着统一的处理逻辑 ------ 就像数学题有固定公式一样,我们把这些逻辑提炼成 "公式",后面推导实现就有了明确方向。
先明确 Promise.resolve
的核心是 "接收一个参数(记为 x
),返回一个 Promise 实例(记为 P
)",所有场景的处理规则都围绕 "x
是什么类型" 展开,最终总结出 3 条核心 "公式":
:::warning
- 若 x 是基本类型 / 普通引用数据类型 / 空值 / 无参数 → Promise.resolve(x) = 成功态 Promise(结果为 x,无参数时 x 为 undefined)
- 若 x 是 Promise 实例 → Promise.resolve(x) = x(直接返回 x,不额外包装)
- 若 x 是 thenable 对象(带 then 方法) → 新建 Promise 实例 P,执行 x.then(P 的 resolve),抛错则调用 P 的 reject,最终返回 P
:::
根据"公式"推导出实现逻辑
要实现 Promise.resolve
,核心是把前面总结的 4 条"公式"翻译成代码逻辑------先判断参数 x
的类型,再按对应规则处理,最终返回符合要求的 Promise 实例。下面按公式顺序逐步推导实现思路:
第一步:搭建基础结构
Promise.resolve
是静态方法,不需要通过实例调用,所以先确定它的基础形式:
直接在 Promise
构造函数上挂载 resolve
方法,方法接收参数 x
,内部返回一个 Promise 实例(或 x
本身,按公式 2 处理)。
javascript
Promise.resolve = function (x) {
// 后续逻辑按公式判断 x 类型,处理后返回对应结果
};
第二步:实现公式 1:x 是基本类型/普通引用类型/空值/无参数
当 x
不是 Promise 实例,也不是 thenable 对象时(即基本类型、普通对象、null
、undefined
,或没传 x
),按公式 1 要求,返回一个"成功态"的 Promise,结果为 x
(无参数时 x
是 undefined
)。
判断逻辑:先排除公式 2 和公式 3 的情况(后续处理),剩下的都走公式 1,直接用 new Promise
新建实例并调用 resolve(x)
。
javascript
Promise.resolve = function (x) {
// 先处理公式2和公式3,剩下的走公式1
// ...(公式2、3逻辑后续加)
// 公式1:返回成功态 Promise,结果为 x
return new Promise((resolve) => {
// 此处(内部):直接调用 resolve x,外部就能通过 .then 并拿到 x 值
// 无参数时 x 是 undefined
resolve(x);
});
};
第三步:实现公式 2:x 是 Promise 实例
按公式 2 要求,若 x
本身就是 Promise 实例,直接返回 x
,不额外包装。
判断逻辑:用 x instanceof Promise
检查 x
是否为 Promise 实例,若是则直接 return x
,跳过后续逻辑。
javascript
Promise.resolve = function (x) {
// 公式2:x 是 Promise 实例 → 返回 x 本身
if (x instanceof Promise) {
return x;
}
// 公式1逻辑(后续加公式3后,公式1放在最后)
// ...
};
第四步:实现公式 3:x 是 thenable 对象(带 then 方法)
按公式 3 要求,x
是带 then
方法的对象时,需新建 Promise 实例 P
,执行 x.then(P的resolve)
,若 x.then
内部抛错,则调用 P的reject
,最终返回 P
。
关键逻辑拆解:
- 判断
x
是否为 thenable:先确保x
是对象或函数(避免非对象调用then
报错),且x.then
是函数; - 新建 Promise 实例
P
,在P
的执行器里调用x.then
; - 把
P
的resolve
传给x.then
(让x.then
内部能触发P
的成功态); - 给
x.then
加catch
捕获抛错,若出错则调用P
的reject
(触发P
的失败态); - 返回新建的
P
。
代码实现:
javascript
Promise.resolve = function (x) {
// 公式2:x 是 Promise 实例 → 返回 x
if (x instanceof Promise) {
return x;
}
// 公式3:x 是 thenable 对象(带 then 方法的对象)
// 注意:typeof null 也等于 'object'
if (typeof x === "object" && x !== null) {
// 检查 x 是否有 then 方法,且 then 是函数
if (typeof x.then === "function") {
return new Promise((resolve, reject) => {
// 执行 x.then,把 P 的 resolve 传进去
x.then(resolve)
// 捕获 x.then 内部的抛错,调用 P 的 reject
.catch(reject);
});
}
}
// 公式1:剩下的情况 → 返回成功态 Promise
return new Promise((resolve) => {
resolve(x);
});
};
最终完整代码实现
将上述逻辑整合,得到 Promise.resolve
的完整实现,能覆盖所有场景:
javascript
// 仅实现 Promise.resolve 静态方法
Promise.resolve = function (x) {
// 情况1:若参数x是Promise实例,直接返回x(避免重复包装)
if (x instanceof Promise) {
return x;
}
// 情况2:若参数x是thenable对象(带then方法的对象)
if ((typeof x === "object" && x !== null) || typeof x === "function") {
// 确认x的then属性是函数,才按thenable处理
if (typeof x.then === "function") {
// 新建Promise实例,执行thenable的then方法
return new Promise((resolve, reject) => {
try {
// 调用thenable的then,传递resolve和reject
x.then(resolve, reject);
} catch (error) {
// 捕获then方法执行时的抛错,触发reject
reject(error);
}
});
}
}
// 情况3:其他类型参数(基础类型、null、undefined等)
// 直接返回成功态Promise,结果为x
return new Promise((resolve) => {
resolve(x);
});
};
"公式"概括总结
Promise.resolve
的实现本质,就是对我们之前提炼的 3 条核心"公式"的代码化翻译,每一段逻辑都对应一条规则:
:::warning
- 若 x 是基本类型 / 普通引用数据类型 / 空值 / 无参数 → Promise.resolve(x) = 成功态 Promise(结果为 x,无参数时 x 为 undefined)
- 若 x 是 Promise 实例 → Promise.resolve(x) = x(直接返回 x,不额外包装)
- 若 x 是 thenable 对象(带 then 方法) → 新建 Promise 实例 P,执行 x.then(P 的 resolve),抛错则调用 P 的 reject,最终返回 P
:::
记住这 3 条"分类处理"的公式,即使长时间不写代码,也能通过"判断类型 → 按规则处理"的步骤一步步推导出实现逻辑。这种将复杂逻辑提炼为简洁公式的方法,同样适用于其他前端手写题(如 Promise.reject
、Promise.all
),是提升代码记忆效率的有效手段。