手写Promise-then的基础实现

手写 Promise 构造函数一文中,我们已经搭建了 Promise 的基本结构。本文将深入探讨 Promise 的核心------then 方法的实现。在面试中,能否写出符合规范的 then 方法往往是区分开发者对 Promise 理解深浅的关键。让我们沿着 Promise A+ 规范的要求,逐步实现一个完整的 then 方法。

then 方法的基本要求

在开始编码之前,我们需要明确 then 方法的几个核心特性:

  • 原型方法then 是 Promise 原型上的方法,且不可枚举
  • 参数处理 :接收两个可选参数(onFulfilledonRejected),均为函数类型
  • 链式调用:必须返回一个新的 Promise 实例,支持链式调用
  • 调用限制:必须通过 Promise 实例调用,直接通过原型调用会报错

基于这些要求,我们可以搭建出 then 方法的基本框架:

js 复制代码
(function () {
  "use strict";
  
  // 状态常量定义
  var PENDING = "pending";
  var FULFILLED = "fulfilled";
  var REJECTED = "rejected";
  
  function Promise(executor) {
    // 构造函数实现(详见上篇文章)
  }
  
  var prototype = Promise.prototype;
  
  // 定义对象属性的配置项(不可枚举)
  function definePropertyConfig(object, key, value) {
    Object.defineProperty(object, key, {
      value: value,
      writable: true,
      enumerable: false,
      configurable: true,
    });
  }
  
  // 添加 Symbol.toStringTag 属性(增强调试体验)
  if (typeof Symbol !== "undefined") {
    definePropertyConfig(prototype, Symbol.toStringTag, "Promise");
  }
  
  // 检查调用者是否为 Promise 实例
  function checkPromiseInstance(value) {
    if (!(value instanceof Promise)) {
      throw new TypeError(
        "Method Promise.prototype.then called on incompatible receiver #<Promise>"
      );
    }
  }
  
  // 定义 then 方法
  definePropertyConfig(prototype, "then", function (onFulfilled, onRejected) {
    checkPromiseInstance(this);
    var self = this;
    
    return new Promise(function (resolve, reject) {
      // 具体实现将在下文展开
    });
  });
})();

处理异步场景

当 Promise 处于异步操作中时(状态为 pending),我们需要将回调函数存储起来,待状态改变后再执行:

js 复制代码
function Promise(executor) {
  var self = this;
  self.state = PENDING;
  self.result = void 0;
  self.onFulfilledCallbacks = []; // 存储成功回调
  self.onRejectedCallbacks = [];  // 存储失败回调
  
  // 状态转换函数
  var changeState = function (state, value) {
    if (self.state !== PENDING) return;
    
    self.state = state;
    self.result = value;
    
    var callbacks = state === FULFILLED 
      ? self.onFulfilledCallbacks 
      : self.onRejectedCallbacks;
    
    // 异步执行所有存储的回调
    setTimeout(function () {
      callbacks.forEach(function (callback) {
        typeof callback === "function" && callback(self.result);
      });
    });
  };
  
  var resolve = function (value) {
    changeState(FULFILLED, value);
  };
  
  var reject = function (reason) {
    changeState(REJECTED, reason);
  };
  
  try {
    executor(resolve, reject);
  } catch (error) {
    reject(error);
  }
}

在 then 方法中对应处理 pending 状态:

js 复制代码
definePropertyConfig(prototype, "then", function (onFulfilled, onRejected) {
  checkPromiseInstance(this);
  var self = this;
  
  return new Promise(function (resolve, reject) {
    if (self.state === FULFILLED) {
      // 处理已完成状态
    } else if (self.state === REJECTED) {
      // 处理已拒绝状态
    } else {
      // 存储回调,等待状态改变
      self.onFulfilledCallbacks.push(function () {
        onFulfilled(self.result);
      });
      
      self.onRejectedCallbacks.push(function () {
        onRejected(self.result);
      });
    }
  });
});

异步执行与错误处理

根据 Promise A+ 规范,then 的回调应该异步执行。我们使用 setTimeout 来模拟微任务队列:

js 复制代码
definePropertyConfig(prototype, "then", function (onFulfilled, onRejected) {
  checkPromiseInstance(this);
  var self = this;
  
  // 统一处理回调函数
  function handleCallback(value, callback, resolve, reject) {
    setTimeout(function () {
      try {
        var result = callback(value);
        resolve(result); // 暂时简单处理
      } catch (error) {
        reject(error);   // 捕获同步错误
      }
    }, 0);
  }
  
  return new Promise(function (resolve, reject) {
    if (self.state === FULFILLED) {
      handleCallback(self.result, onFulfilled, resolve, reject);
    } else if (self.state === REJECTED) {
      handleCallback(self.result, onRejected, resolve, reject);
    } else {
      self.onFulfilledCallbacks.push(function (value) {
        handleCallback(value, onFulfilled, resolve, reject);
      });
      
      self.onRejectedCallbacks.push(function (value) {
        handleCallback(value, onRejected, resolve, reject);
      });
    }
  });
});

实现值穿透特性

值穿透是指在 Promise 链中.then方法没有提供相应的回调函数(onFulfilled或者onRejected)或者不是一个函数类型,Promise会自动创建一个回调函数将这个值传递下去。

js 复制代码
definePropertyConfig(prototype, "then", function (onFulfilled, onRejected) {
  checkPromiseInstance(this);
  var self = this;
  
  // 值穿透处理
  onFulfilled = typeof onFulfilled === "function" 
    ? onFulfilled 
    : function (value) { return value; }; // 传递值
    
  onRejected = typeof onRejected === "function" 
    ? onRejected 
    : function (reason) { throw reason; }; // 抛出异常
  
  // 其余代码保持不变
});

完整代码

js 复制代码
(function () {
  "use strict";
  
  var PENDING = "pending";
  var FULFILLED = "fulfilled";
  var REJECTED = "rejected";
  
  function Promise(executor) {
    var self = this;
    
    // 参数校验
    if (typeof executor !== "function") {
      throw new TypeError(`Promise resolver ${executor} is not a function`);
    }
    if (!(self instanceof Promise)) {
      throw new TypeError("Promise constructor must be called with 'new'");
    }
    
    self.state = PENDING;
    self.result = void 0;
    self.onFulfilledCallbacks = [];
    self.onRejectedCallbacks = [];
    
    var changeState = function (state, value) {
      if (self.state !== PENDING) return;
      
      self.state = state;
      self.result = value;
      
      var callbacks = state === FULFILLED 
        ? self.onFulfilledCallbacks 
        : self.onRejectedCallbacks;
      
      setTimeout(function () {
        callbacks.forEach(function (callback) {
          typeof callback === "function" && callback(self.result);
        });
      });
    };
    
    var resolve = function (value) {
      changeState(FULFILLED, value);
    };
    
    var reject = function (reason) {
      changeState(REJECTED, reason);
    };
    
    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }
  
  var prototype = Promise.prototype;
  
  function definePropertyConfig(object, key, value) {
    Object.defineProperty(object, key, {
      value: value,
      writable: true,
      enumerable: false,
      configurable: true,
    });
  }
  
  function checkPromiseInstance(value) {
    if (!(value instanceof Promise)) {
      throw new TypeError(
        "Method Promise.prototype.then called on incompatible receiver #<Promise>"
      );
    }
  }
  
  definePropertyConfig(prototype, "then", function (onFulfilled, onRejected) {
    checkPromiseInstance(this);
    var self = this;
    
    // 值穿透处理
    onFulfilled = typeof onFulfilled === "function" 
      ? onFulfilled 
      : function (value) { return value; };
      
    onRejected = typeof onRejected === "function" 
      ? onRejected 
      : function (reason) { throw reason; };
    
    function handleCallback(value, callback, resolve, reject) {
      setTimeout(function () {
        try {
          var result = callback(value);
          resolve(result);
        } catch (error) {
          reject(error);
        }
      }, 0);
    }
    
    return new Promise(function (resolve, reject) {
      if (self.state === FULFILLED) {
        handleCallback(self.result, onFulfilled, resolve, reject);
      } else if (self.state === REJECTED) {
        handleCallback(self.result, onRejected, resolve, reject);
      } else {
        self.onFulfilledCallbacks.push(function (value) {
          handleCallback(value, onFulfilled, resolve, reject);
        });
        
        self.onRejectedCallbacks.push(function (value) {
          handleCallback(value, onRejected, resolve, reject);
        });
      }
    });
  });
  
  // 环境检测与导出
  if (typeof window !== "undefined") {
    window.SelfPromise = Promise;
  }
  if (typeof module === "object" && typeof module.exports === "object") {
    module.exports = Promise;
  }
})();

小结

本文详细实现了 Promise 的 then 方法,涵盖了:

  1. 基本结构:正确的原型方法定义和链式调用
  2. 状态处理:针对不同状态(fulfilled/rejected/pending)采取不同的处理策略
  3. 异步执行:使用 setTimeout 模拟微任务队列
  4. 错误处理:妥善处理回调函数中的同步错误
  5. 值穿透:实现参数非函数时的默认行为

这个实现已经涵盖了 Promise then 方法的核心功能,但需要注意的是,真正的 Promise 实现还需要处理更复杂的情况,如 thenable 对象的递归解析、循环引用检测等。这些内容我们将在后续文章中继续探讨。

通过手写实现,我们不仅能够更深入地理解 Promise 的工作原理,也能够在面试中展现出对 JavaScript 异步编程的深刻理解。

相关推荐
June_liu2 小时前
列太多vxe-table自动启用横向虚拟滚动引起的bug
前端·javascript
齐杰拉2 小时前
useSse 开源:如何把流式数据请求/处理简化到极致
前端·chatgpt
起风了啰2 小时前
Android & IOS兼容性问题
前端
养生达人_zzzz2 小时前
飞书三方登录功能实现与行业思考
前端·javascript·架构
GarrettGao2 小时前
Frida常见用法
javascript·python·逆向
布列瑟农的星空2 小时前
从webpack到vite——配置与特性全面对比
前端
程序员鱼皮2 小时前
我代表编程导航,向大家道歉!
前端·后端·程序员
车前端2 小时前
极致灵活:如何用一个输入框,满足后台千变万化的需求
前端
用户11481867894842 小时前
Rollup构建JavaScript核验库,并发布到NPM
前端