乐于分享
好东西不私藏

手写Promise完整实现(附源码+面试题)

手写Promise完整实现(附源码+面试题)

Promise 是前端异步编程的基石,也是面试中最高频、最容易拉开差距的考点。很多人会用,但一让手写就慌;能写出来,又讲不清原理。

今天这篇,从 0 到 1 实现一个完整 Promise,代码带详细注释,可直接复制运行,最后附上高频面试题答案,面试前看这一篇就够。

适合:准备面试的前端、想夯实基础的同学、零基础进阶。

一、先明确Promise的核心特性

1. Promise有3种状态:
pending(等待中)、fulfilled(成功)、rejected(失败)
2. 状态一旦改变(pending→fulfilled 或 pending→rejected),就不能再修改;
3. 支持链式调用(then方法),then返回一个新的Promise,实现异步流程串联。
4. Promise还有catch(捕获失败)、finally(无论成功失败都执行)、resolve(直接返回成功Promise)、reject(直接返回失败Promise)。

二、从0到1手写Promise(完整源码+注释)

我们分步骤实现,每一步都加详细注释,确保你能看懂每一行代码的作用,复制到VS Code就能直接运行测试。

1. 定义Promise类,初始化状态和回调

// 定义Promise的三种状态(常量,避免魔法值)const PENDING = 'pending';const FULFILLED = 'fulfilled';const REJECTED = 'rejected';// 手写Promise类class MyPromise {  // 构造函数,接收一个执行器函数(executor),立即执行  constructor(executor) {    try {      // 执行器函数接收两个参数:resolve和reject方法      executor(this.resolve, this.reject);    } catch (error) {      // 执行器执行过程中抛出错误,直接触发reject      this.reject(error);    }  }  // 初始状态为pending  status = PENDING;  // 存储成功的结果  value = undefined;  // 存储失败的原因  reason = undefined;  // 存储成功的回调函数(用于异步场景,状态改变时执行)  onFulfilledCallbacks = [];  // 存储失败的回调函数(用于异步场景,状态改变时执行)  onRejectedCallbacks = [];  // 成功方法:将状态改为fulfilled,保存成功结果,执行所有成功回调  resolve = (value) => {    // 状态一旦改变,不能再修改(只有pending状态能改为fulfilled)    if (this.status !== PENDING) return;    // 改变状态为成功    this.status = FULFILLED;    // 保存成功结果    this.value = value;    // 执行所有缓存的成功回调(异步场景下,回调会先存入数组)    while (this.onFulfilledCallbacks.length) {      // 取出回调并执行(shift()取出第一个,确保顺序执行)      this.onFulfilledCallbacks.shift()(value);    }  };  // 失败方法:将状态改为rejected,保存失败原因,执行所有失败回调  reject = (reason) => {    // 状态一旦改变,不能再修改(只有pending状态能改为rejected)    if (this.status !== PENDING) return;    // 改变状态为失败    this.status = REJECTED;    // 保存失败原因    this.reason = reason;    // 执行所有缓存的失败回调    while (this.onRejectedCallbacks.length) {      this.onRejectedCallbacks.shift()(reason);    }  };}

2. 实现then方法(核心,支持链式调用)

then方法是Promise的核心,需要满足两个关键要求:① 接收成功和失败两个回调;② 返回一个新的Promise,实现链式调用。
// 实现then方法,接收两个可选参数:成功回调、失败回调then(onFulfilled, onRejected) {  // 处理回调参数默认值:如果没传成功回调,直接返回结果;没传失败回调,直接抛出错误  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value;  onRejected = typeof onRejected === 'function' ? onRejected : (reason) => { throw reason };  // 返回一个新的Promise,实现链式调用  const newPromise = new MyPromise((resolve, reject) => {    // 1. 当状态为fulfilled时,执行成功回调    if (this.status === FULFILLED) {      // 用setTimeout模拟异步(符合Promise的异步特性,即使状态已确定,回调也会异步执行)      setTimeout(() => {        try {          // 执行成功回调,获取回调返回值          const result = onFulfilled(this.value);          // 关键:判断回调返回值是否为Promise,如果是,等待其状态改变;否则直接resolve          this.resolvePromise(newPromise, result, resolve, reject);        } catch (error) {          // 回调执行过程中抛出错误,触发新Promise的reject          reject(error);        }      }, 0);    }    // 2. 当状态为rejected时,执行失败回调    if (this.status === REJECTED) {      setTimeout(() => {        try {          const result = onRejected(this.reason);          this.resolvePromise(newPromise, result, resolve, reject);        } catch (error) {          reject(error);        }      }, 0);    }    // 3. 当状态为pending时(异步场景,如定时器),将回调存入数组缓存    if (this.status === PENDING) {      // 缓存成功回调,后续状态改变时执行      this.onFulfilledCallbacks.push(() => {        setTimeout(() => {          try {            const result = onFulfilled(this.value);            this.resolvePromise(newPromise, result, resolve, reject);          } catch (error) {            reject(error);          }        }, 0);      });      // 缓存失败回调      this.onRejectedCallbacks.push(() => {        setTimeout(() => {          try {            const result = onRejected(this.reason);            this.resolvePromise(newPromise, result, resolve, reject);          } catch (error) {            reject(error);          }        }, 0);      });    }  });  // 返回新的Promise,支持链式调用  return newPromise;}// 辅助方法:处理then回调返回值,判断是否为Promise,实现链式调用的核心逻辑resolvePromise(newPromise, result, resolve, reject) {  // 避免循环引用(如果回调返回的Promise就是当前新创建的Promise,直接报错)  if (newPromise === result) {    return reject(new TypeError('Chaining cycle detected for promise #<MyPromise>'));  }  // 处理 thenable(包括 Promise 和其他有 then 方法的对象)  if (result !== null && (typeof result === 'object' || typeof result === 'function')) {    try {      const then = result.then;      if (typeof then === 'function') {        // 是 thenable,异步调用 then        then.call(result, resolve, reject);        return;      }    } catch (error) {      return reject(error);    }  }  resolve(result);}

3. 实现catch、finally、resolve、reject方法

这几个方法是Promise的常用扩展,基于上面的代码,轻松就能实现,也是面试中常考的补充点。
// 实现catch方法:捕获Promise的失败状态,本质是then方法的语法糖(只传失败回调)catch(onRejected) {  return this.then(undefined, onRejected);}// 实现finally方法:无论Promise成功还是失败,都会执行(不接收参数,不改变Promise结果)finally(callback) {  return this.then(    // 成功时,先执行callback,再返回结果    (value) => MyPromise.resolve(callback()).then(() => value),    // 失败时,先执行callback,再抛出错误    (reason) => MyPromise.resolve(callback()).then(() => { throw reason; })  );}// 实现静态resolve方法:直接返回一个成功状态的Promisestatic resolve(value) {  // 如果传入的是Promise,直接返回;否则返回一个成功的Promise  if (value instanceof MyPromisereturn value;  return new MyPromise((resolve) => resolve(value));}// 实现静态reject方法:直接返回一个失败状态的Promisestatic reject(reason) {  return new MyPromise((resolve, reject) => reject(reason));}

4. 完整源码(可直接复制运行)

将上面的代码整合,复制到VS Code,就能测试使用,和原生Promise用法完全一致。
// 完整手写Promise源码(可直接复制运行)const PENDING = 'pending';const FULFILLED = 'fulfilled';const REJECTED = 'rejected';class MyPromise {  constructor(executor) {    try {      executor(this.resolve, this.reject);    } catch (error) {      this.reject(error);    }  }  status = PENDING;  value = undefined;  reason = undefined;  onFulfilledCallbacks = [];  onRejectedCallbacks = [];  resolve = (value) => {    if (this.status !== PENDING) return;    this.status = FULFILLED;    this.value = value;    while (this.onFulfilledCallbacks.length) {      this.onFulfilledCallbacks.shift()(value);    }  };  reject = (reason) => {    if (this.status !== PENDING) return;    this.status = REJECTED;    this.reason = reason;    while (this.onRejectedCallbacks.length) {      this.onRejectedCallbacks.shift()(reason);    }  };  then(onFulfilled, onRejected) {    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value;    onRejected = typeof onRejected === 'function' ? onRejected : (reason) => { throw reason };    const newPromise = new MyPromise((resolve, reject) => {      if (this.status === FULFILLED) {        setTimeout(() => {          try {            const result = onFulfilled(this.value);            this.resolvePromise(newPromise, result, resolve, reject);          } catch (error) {            reject(error);          }        }, 0);      }      if (this.status === REJECTED) {        setTimeout(() => {          try {            const result = onRejected(this.reason);            this.resolvePromise(newPromise, result, resolve, reject);          } catch (error) {            reject(error);          }        }, 0);      }      if (this.status === PENDING) {        this.onFulfilledCallbacks.push(() => {          setTimeout(() => {            try {              const result = onFulfilled(this.value);              this.resolvePromise(newPromise, result, resolve, reject);            } catch (error) {              reject(error);            }          }, 0);        });        this.onRejectedCallbacks.push(() => {          setTimeout(() => {            try {              const result = onRejected(this.reason);              this.resolvePromise(newPromise, result, resolve, reject);            } catch (error) {              reject(error);            }          }, 0);        });      }    });    return newPromise;  }  resolvePromise(newPromise, result, resolve, reject) {    if (newPromise === result) {      return reject(new TypeError('Chaining cycle detected for promise #<MyPromise>'));    }    if (result !== null && (typeof result === 'object' || typeof result === 'function')) {      try {        const then = result.then;        if (typeof then === 'function') {          // 是 thenable,异步调用 then          then.call(result, resolve, reject);          return;        }      } catch (error) {        return reject(error);      }    }    resolve(result);  }  catch(onRejected) {    return this.then(undefined, onRejected);  }  finally(callback) {    return this.then(      (value) => MyPromise.resolve(callback()).then(() => value),      (reason) => MyPromise.resolve(callback()).then(() => { throw reason; })    );  }  static resolve(value) {    if (value instanceof MyPromise) return value;    return new MyPromise((resolve) => resolve(value));  }  static reject(reason) {    return new MyPromise((resolve, reject) => reject(reason));  }}// 测试代码(可删除,用于验证功能)// MyPromise.resolve('成功').then(res => console.log(res)).catch(err => console.log(err)).finally(() => console.log('执行完毕'));// new MyPromise((resolve, reject) => {//   setTimeout(() => resolve('异步成功'), 1000);// }).then(res => res + '123').then(res => console.log(res));

三、测试验证(确保代码可用)

复制上面的完整源码,在VS Code中运行以下测试代码,验证我们手写的Promise是否和原生Promise功能一致:
// 测试1:基本成功场景new MyPromise((resolve, reject) => {  resolve('测试成功');}).then(res => {  console.log(res); // 输出:测试成功}).finally(() => {  console.log('测试1执行完毕'); // 输出:测试1执行完毕});// 测试2:异步场景(定时器)new MyPromise((resolve, reject) => {  setTimeout(() => {    resolve('异步测试成功');  }, 1000);}).then(res => {  return res + ',链式调用';}).then(res => {  console.log(res); // 1秒后输出:异步测试成功,链式调用}).catch(err => {  console.log(err);});// 测试3:失败场景new MyPromise((resolve, reject) => {  reject('测试失败');}).catch(err => {  console.log(err); // 输出:测试失败}).finally(() => {  console.log('测试3执行完毕'); // 输出:测试3执行完毕});// 测试4:静态方法MyPromise.resolve('静态resolve成功').then(res => console.log(res)); // 输出:静态resolve成功MyPromise.reject('静态reject失败').catch(err => console.log(err)); // 输出:静态reject失败
运行后,所有测试用例都能正常输出,说明我们的手写Promise完全可用,满足日常使用和面试要求。

四、高频面试题(手写Promise必问)

写完代码,还要掌握这些面试考点,面试官大概率会追问,直接背答案即可:
问:Promise的三种状态是什么?能否相互转换?
答:三种状态:pending(等待)、fulfilled(成功)、rejected(失败);只能从pending→fulfilled、pending→rejected,状态一旦改变,无法再修改。
问:then方法为什么能实现链式调用?
答:因为then方法会返回一个新的Promise,而不是this,所以可以继续调用then/catch,实现异步流程串联。
问:手写Promise时,为什么要用setTimeout?
答:为了模拟Promise的异步特性——即使Promise的状态已经确定(比如同步resolve),then回调也会异步执行,符合原生Promise的规范。
问:如何处理then回调返回的Promise?
答:在resolvePromise辅助方法中判断,如果回调返回值是Promise,就等待其状态改变,再将结果传递给下一个then;否则直接resolve结果。
问:Promise的catch方法本质是什么?
答:catch是then方法的语法糖,本质是调用then(undefined, onRejected),专门捕获Promise的失败状态。

问:什么是 thenable?为什么要处理 thenable 而不是只判断 Promise 实例?

答:thenable 是指有 then 方法的对象或函数。Promise/A+ 规范要求兼容所有 thenable,而不只是原生 Promise 实例,这是鸭子类型的体现,确保不同库之间的 Promise 可以互操作。

五、总结

手写Promise的核心,就是抓住“状态不可变”和“链式调用”两个关键点。
如果想进一步巩固,还可以尝试实现Promise.all、Promise.race等静态方法(评论区留言,后续补充)。

如果你觉得文章内容有用,请记得点赞收藏、转发

点击面的公众号名片直接关注