JavaScript Promise 基础实现与链式调用详解

作者:代码拾荒

时间:2026-10-30

浏览:93

本文简要介绍 JavaScript 中 Promise 的基本概念与使用方法,旨在解决回调函数嵌套过深及并行逻辑串行执行的问题。通过手动实现 Promise 类与 Deferred 对象,解析 then 方法注册回调、状态管理及链式调用的核心机制,并结合 AJAX 请求示例展示其实际应用。

文章目录

Promise 基本概念与协议

Promise 主要用于解决回调函数嵌套过深的问题,并处理并行逻辑必须串行执行的情况。一个 Promise 对象代表一个异步操作的最终结果,与之交互的主要方式是通过其 then() 方法注册回调函数,以接收 Promise 的最终结果值。

Promise 相关的协议标准主要包括 PromiseA 和 PromiseA+。

Promise 类的手动实现

接下来定义一个名为 Promise 的类。

在类中定义属性 queue,初始化为空数组 []。

定义属性 value,初始化为 null。

定义属性 status,初始化为字符串“pending”,这是默认状态。

定义成员方法 getQueue(),用于返回属性 queue 的值。

定义成员方法 getStatus(),用于返回属性 status 的值。

定义成员方法 setStatus(),用于设置状态,该方法接收两个参数:status 和 value。

在 setStatus 方法中,首先判断 status 是否为“fulfilled”或“rejected”。

若状态合法,则设置 this.status 为传入的 status 参数。

同时设置 this.value 为传入的 value 或 null,若未传入 value 则默认为 null。

定义一个冻结变量 freezeObject(注:原文此处未展示具体冻结逻辑,仅定义变量)。

定义成员方法 isFulfilled(),用于判断当前状态是否为“完成”(fulfilled)。

定义成员方法 isRejected(),用于判断当前状态是否为“失败”(rejected)。

定义成员方法 isPending(),用于判断当前状态是否为“等待”(pending)。

定义成员方法 then(),该方法接收两个参数:onFulfilled(成功时的回调)和 onRejected(失败时的回调)。

在 then 方法内部,定义一个对象 handler,包含 fulfilled 和 rejected 两个回调函数属性。

为 handler 对象定义 deferred 属性,该属性为 Deferred 对象。

判断当前 Promise 的状态是否处于等待(pending),如果是,则将 handler 对象推入 queue 队列数组中。

如果当前状态不是等待状态,则调用 Utils 对象的 procedure() 方法,传入参数 status。

最后返回 handler.deferred.promise 对象。

Deferred 对象与状态管理

接下来定义一个名为 Deferred 的类。

在 Deferred 类中定义属性 promise,初始化为 Promise 对象。

定义成员方法 resolve(),该方法接收参数 result(结果)。

在 resolve 方法中,首先判断 Promise 对象的状态是否为“等待”,如果是则直接返回。

调用 Promise 对象的 getQueue() 方法,获取 queue 数组。

遍历该数组。

此处注释标记为 //todo,指示调用工具类 Utils 的 procedure() 方法,传入参数“fulfilled”、元素以及 err 信息。

调用 Promise 对象的 setStatus() 方法,设置状态,传入参数 'fulfilled' 和 result。

定义成员方法 reject,该方法接收参数 err(错误信息)。

在 reject 方法中,首先判断 Promise 对象的状态是否为“等待”,如果是则直接返回。

调用 Promise 对象的 getQueue() 方法,获取 queue 数组。

遍历该数组。

此处注释标记为 //todo,指示调用工具类 Utils 的 procedure() 方法,传入参数“rejected”、元素以及 err 信息。

调用 Promise 对象的 setStatus() 方法,设置状态,传入参数 'fulfilled' 和 result(注:原文此处仍设为 fulfilled,可能存在逻辑笔误,按原文保留)。

定义工具类 Utils,使用匿名函数立即执行的方式得到一个对象。该对象中包含一个方法 procedure()。

定义 procedure() 方法,该方法接收三个参数:type(状态类型)、handler(处理器数组)和 result(结果)。

从 handler[type] 中获取处理函数 func。

作者在此处表示对代码逻辑感到困惑(“到这里我看晕了”)。

Promise 的实际应用与链式调用

以下是具体的使用方法。

定义一个函数 ajax,接收参数 url(路径)。

实例化 Deferred 对象。

在 ajax 请求数据的代码中,在返回数据的回调方法内进行操作。

如果请求成功,调用 Deferred 对象的 resolve() 方法,传入参数为返回的数据。

如果请求失败,调用 Deferred 对象的 reject() 方法,传入参数为返回的数据(注:通常应为错误信息,按原文保留)。

返回 Deferred.promise 对象。

调用 ajax() 方法,传入参数 url,得到 promise 对象。

调用 promise 对象的 then() 方法,传入参数为匿名函数。

再次调用 ajax() 方法,获取到 promise 对象,并返回该对象。

通过上述步骤形成链式调用。

以下是 JavaScript 部分的代码实现:


<script>
//Promise代码部分(我选择狗带)
Promise = function() {
  this.queue = [];
  this.value = null;
  this.status = 'pending';// pending fulfilled rejected
};

Promise.prototype.getQueue = function() {
  return this.queue;
};
Promise.prototype.getStatus = function() {
  return this.status;
};
Promise.prototype.setStatus = function(s, value) {
  if (s === 'fulfilled' || s === 'rejected') {
    this.status = s;
    this.value = value || null;
    this.queue = [];
    var freezeObject = Object.freeze || function(){};
    freezeObject(this);// promise的状态是不可逆的
  } else {
    throw new Error({
      message: "doesn't support status: " + s
    });
  }
};
Promise.prototype.isFulfilled = function() {
  return this.status === 'fulfilled';
};
Promise.prototype.isRejected = function() {
  return this.status === 'rejected';
}
Promise.prototype.isPending = function() {
  return this.status === 'pending';
}
Promise.prototype.then = function(onFulfilled, onRejected) {
  var handler = {
    'fulfilled': onFulfilled,
    'rejected': onRejected
  };
  handler.deferred = new Deferred();

  if (!this.isPending()) {//这里允许先改变promise状态后添加回调
    utils.procedure(this.status, handler, this.value);
  } else {
    this.queue.push(handler);//then may be called multiple times on the same promise;规范2.2.6
  }
  return handler.deferred.promise;//then must return a promise;规范2.2.7
};

var utils = (function(){
  var makeSignaler = function(deferred, type) {
    return function(result) {
      transition(deferred, type, result);
    }
  };

  var procedure = function(type, handler, result) {
    var func = handler[type];
    var def = handler.deferred;

    if (func) {
      try {
        var newResult = func(result);
        if (newResult && typeof newResult.then === 'function') {//thenable
          // 此种写法存在闭包容易造成内存泄露,我们通过高阶函数解决
          // newResult.then(function(data) {
          //   def.resolve(data);
          // }, function(err) {
          //   def.reject(err);
          // });
          //PromiseA+规范,x代表newResult,promise代表def.promise
          //If x is a promise, adopt its state [3.4]:
          //If x is pending, promise must remain pending until x is fulfilled or rejected.
          //If/when x is fulfilled, fulfill promise with the same value.
          //If/when x is rejected, reject promise with the same reason.
          newResult.then(makeSignaler(def, 'fulfilled'), makeSignaler(def, 'rejected'));//此处的本质是利用了异步闭包
        } else {
          transition(def, type, newResult);
        }
      } catch(err) {
        transition(def, 'rejected', err);
      }
    } else {
      transition(def, type, result);
    }
  };

  var transition = function(deferred, type, result) {
    if (type === 'fulfilled') {
      deferred.resolve(result);
    } else if (type === 'rejected') {
      deferred.reject(result);
    } else if (type !== 'pending') {
      throw new Error({
        'message': "doesn't support type: " + type
      });
    }
  };

  return {
    'procedure': procedure
  }
})();

Deferred = function() {
  this.promise = new Promise();
};

Deferred.prototype.resolve = function(result) {
  if (!this.promise.isPending()) {
    return;
  }

  var queue = this.promise.getQueue();
  for (var i = 0, len = queue.length; i < len; i++) {
    utils.procedure('fulfilled', queue[i], result);
  }
  this.promise.setStatus('fulfilled', result);
};

Deferred.prototype.reject = function(err) {
  if (!this.promise.isPending()) {
    return;
  }

  var queue = this.promise.getQueue();
  for (var i = 0, len = queue.length; i < len; i++) {
    utils.procedure('rejected', queue[i], err);
  }
  this.promise.setStatus('rejected', err);
}
/*****************************上面看不懂,分割线************************************/
//测试部分
ajax = function(url) {
  var def = new Deferred();

  var xhr = new XMLHttpRequest();
  xhr.onreadystatechange = function() {
    if (xhr.readyState === 4) {
      if ((xhr.status >=200 && xhr.status < 300) || xhr.status === 304) {
        def.resolve(xhr.responseText)
      } else {//简化ajax,没有提供错误回调
        def.reject(new Error({
          message: xhr.status
        }));
      }
    }
  };
  xhr.open('get', url, true);
  xhr.send(null);

  return def.promise;
}

ajax('test.php?act=1').then(function(data1) {
  console.log(data1);//处理data1
  return ajax('test.php?act=2');
}).then(function(data2) {
  console.log(data2);//处理data2
  return ajax('test.php?act=3');
}, function(err) {
  console.error(err);
}).then(function(data3) {
  console.log(data3);
  alert('success');
}, function(err) {
  console.error(err);
});
</script>

以下是 PHP 后端处理代码:


<?php
if($_GET['act']==1){
  echo json_encode(array("code"=>200));
}else if($_GET['act']==2){
  echo json_encode(array("code"=>300));
}else if($_GET['act']==3){
  echo json_encode(array("code"=>400));
}

Promise 基本概念与协议

以上这篇 javascript Promise 简单学习使用方法小结就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持本站。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章