手写 Promise.resolve:从使用场景到实现的完整推导

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 方法,这里有两个关键逻辑要讲清楚:

  1. then 方法只认一个参数 :thenable 的 then 方法只需要接收一个参数,这个参数是 resolve 回调(可以理解成 "成功时要执行的函数")。

:::warning 当 Promise.resolve 处理它时,会主动调用这个 then 方法,并且把自己的 resolve 传进去 ------ 只要在 then 里调用了这个 resolve,后续 .then 就能拿到值。

:::

  1. 失败怎么处理?靠抛错 :因为 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 就干两件事:

  1. 调用 thenable 的 then 方法,并传递自身的 resolve 回调
  2. 捕获 thenable 的 then 方法内部抛错,交给自身的 reject 处理

从场景总结的"数学公式"

前面梳理了 Promise.resolve 的各种使用场景,其实这些场景背后藏着统一的处理逻辑 ------ 就像数学题有固定公式一样,我们把这些逻辑提炼成 "公式",后面推导实现就有了明确方向。

先明确 Promise.resolve 的核心是 "接收一个参数(记为 x),返回一个 Promise 实例(记为 P)",所有场景的处理规则都围绕 "x 是什么类型" 展开,最终总结出 3 条核心 "公式":

:::warning

  1. 若 x 是基本类型 / 普通引用数据类型 / 空值 / 无参数 → Promise.resolve(x) = 成功态 Promise(结果为 x,无参数时 x 为 undefined)
  2. 若 x 是 Promise 实例 → Promise.resolve(x) = x(直接返回 x,不额外包装)
  3. 若 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 对象时(即基本类型、普通对象、nullundefined,或没传 x),按公式 1 要求,返回一个"成功态"的 Promise,结果为 x(无参数时 xundefined)。

判断逻辑:先排除公式 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

关键逻辑拆解:

  1. 判断 x 是否为 thenable:先确保 x 是对象或函数(避免非对象调用 then 报错),且 x.then 是函数;
  2. 新建 Promise 实例 P,在 P 的执行器里调用 x.then
  3. Presolve 传给 x.then(让 x.then 内部能触发 P 的成功态);
  4. x.thencatch 捕获抛错,若出错则调用 Preject(触发 P 的失败态);
  5. 返回新建的 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

  1. 若 x 是基本类型 / 普通引用数据类型 / 空值 / 无参数 → Promise.resolve(x) = 成功态 Promise(结果为 x,无参数时 x 为 undefined)
  2. 若 x 是 Promise 实例 → Promise.resolve(x) = x(直接返回 x,不额外包装)
  3. 若 x 是 thenable 对象(带 then 方法) → 新建 Promise 实例 P,执行 x.then(P 的 resolve),抛错则调用 P 的 reject,最终返回 P

:::

记住这 3 条"分类处理"的公式,即使长时间不写代码,也能通过"判断类型 → 按规则处理"的步骤一步步推导出实现逻辑。这种将复杂逻辑提炼为简洁公式的方法,同样适用于其他前端手写题(如 Promise.rejectPromise.all),是提升代码记忆效率的有效手段。

相关推荐
golang学习记4 小时前
从0死磕全栈之Next.js 自定义 Server 指南:何时使用及如何实现
前端
张可爱4 小时前
从奶茶店悟透 JavaScript:递归、继承、浮点数精度、尾递归全解析(通俗易懂版)
前端
梵得儿SHI4 小时前
Vue 开发环境搭建全指南:从工具准备到项目启动
前端·javascript·vue.js·node.js·pnpm·vue开发环境·nvm版本管理
八月ouc4 小时前
每日小知识点:10.14 webpack 有几种文件指纹
前端·webpack
苏琢玉4 小时前
从 Hexo 到 Astro:重构我的个人博客
前端·hexo
Glommer4 小时前
某音 Js 逆向思路
javascript·逆向
街尾杂货店&4 小时前
webpack - 单独打包指定JS文件(因为不确定打出的前端包所访问的后端IP,需要对项目中IP配置文件单独拿出来,方便运维部署的时候对IP做修改)
前端·javascript·webpack
月光技术杂谈4 小时前
用Deepseek 实现一个基于web的扣图应用
前端·javascript·html5·ccs·tensorflow.js·canvas api