手写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 异步编程的深刻理解。

相关推荐
Asmewill几秒前
DeepAgents学习笔记三(Backend记忆存储)
前端
Alan Lu Pop17 分钟前
前端开发助手
前端·智能体
程序员鱼皮18 分钟前
我用 GitHub 仓库养 AI 龙虾,自动开发上线项目!保姆级教程
前端·人工智能·ai·程序员·github·编程·ai编程
276695829231 分钟前
京东随机变速滑块拼图验证码识别(京东E卡)
java·服务器·前端·python·京东滑块·京东变速滑块·京东e卡绑卡
এ慕ོ冬℘゜1 小时前
手写生产级 jQuery Toast 轻量提示组件|零插件依赖、动画流畅、极简高
前端·javascript·jquery
Oneslide1 小时前
UI设计-企业OA风格
前端
程序员海军1 小时前
我用了 8 个月 Codex CLI,总结出这套 AI 编程工作流
前端·后端·aigc
大家的林语冰1 小时前
Express 团队官宣:全新网站正式上线,Logo 重做,支持两个主版本文档无缝切换!
javascript·node.js·express
এ慕ོ冬℘゜2 小时前
手写一款高兼容、零BUG图片预览组件|前端
前端·bug
铁链鞭策大师2 小时前
javaEE之多线程(2)
java·前端·java-ee