Promise实现原理

这两天在熟悉 kissy框架的时候,看到了 Promise 模块。 Promise 对于一个Jser并不陌生, Promise 类似于一个事务管理器,它的作用就是将各种内嵌回调的事务用流水形式表达。利用 Promise 可以让异步编程更符合人的直觉,让代码逻辑更加清晰,把开发人员从回调地狱中释放出来。这么“高大上”的东西,以前写 nodejs 代码的时候只是简单的用用,还没有理解其基本的实现原理,罪过!个人认为,理解编程思想最好的途径就是阅读一份简易的实现源码。很幸运,网上有不少 Promise 的简易实现,其中 这篇博文介绍的实现方式非常赞,下面就来好好研究下吧!

基础概念

目前, Promise 是 ECMAScript 6 规范的重要特性之一,各大浏览器也开始慢慢支持这一特性。当然,也有一些第三方内库实现了该功能,如: Q、 when、 WinJS、 RSVP.js等。

Promise 对象用来进行延迟( deferred )和异步( asynchronous )计算。一个 Promise 处于以下四种状态之一:

  • pending: 还没有得到肯定或者失败结果,进行中
  • fulfilled: 成功的操作
  • rejected: 失败的操作
  • settled: 已被 fulfilled 或 rejected

Promise 对象有两个重要的方法,一个是 then ,另一个是 resolve 

  • then:将事务添加到事务队列中
  • resolve:开启流程,让整个操作从第一个事务开始执行

Promise 常用方式如下:

var p = new Promise(function(resolve, reject) {
    ...
    // 事务触发
    resovle(xxx);
    ...
});
p.then(function(value) {
   // 满足
  }, function(reason) {
  // 拒绝
}).then().then()...

示意图如下:

实现步骤

1. Promise 其实就是一个状态机。按照它的定义,我们可从如下基础代码开始:

var PENDING = 0;  // 进行中
var FULFILLED = 1; // 成功
var REJECTED = 2;  // 失败

function Promise() {
  // 存储PENDING, FULFILLED或者REJECTED的状态
  var state = PENDING;

  // 存储成功或失败的结果值
  var value = null;

  // 存储成功或失败的处理程序,通过调用`.then`或者`.done`方法
  var handlers = [];

  // 成功状态变化
  function fulfill(result) {
    state = FULFILLED;
    value = result;
  }
  // 失败状态变化
  function reject(error) {
    state = REJECTED;
    value = error;
  }
}

2.下面是 Promise 的 resolve 方法实现:

注意: resolve 方法可接收的参数有两种:一个普通的值/对象或者一个 Promise 对象。如果是普通的值/对象,则直接把结果传递到下一个对象;如果是一个 Promise 对象,则必须先等待这个子任务序列完成。

function Promise() {
    ...
    function resolve(result) {
      try {
        var then = getThen(result);
        // 如果是一个promise对象
        if (then) {
          doResolve(then.bind(result), resolve, reject);
          return;
        }
        // 修改状态,传递结果到下一个事务
        fulfill(result);
      } catch (e) {
        reject(e);
      }
    }
}

两个辅助方法:

/**
 * Check if a value is a Promise and, if it is,
 * return the `then` method of that promise.
 *
 * @param {Promise|Any} value
 * @return {Function|Null}
 */
function getThen(value) {
    var t = typeof value;
    if (value && (t === ‘object‘ || t === ‘function‘)) {
        var then = value.then;
        if (typeof then === ‘function‘) {
            return then;
        }
    }
    return null;
}

/**
 * Take a potentially misbehaving resolver function and make sure
 * onFulfilled and onRejected are only called once.
 *
 * Makes no guarantees about asynchrony.
 *
 * @param {Function} fn A resolver function that may not be trusted
 * @param {Function} onFulfilled
 * @param {Function} onRejected
 */
 function doResolve(fn, onFulfilled, onRejected) {
     var done = false;
     try {
         fn(function(value) {
             if (done) return;
             done = true;
             onFulfilled(value);
         }, function(reason) {
             if (done) return;
             done = true;
             onRejected(reason);
         });
     } catch(ex) {
         if (done) return;
         done = true;
         onRejected(ex);
     }
 }

3.上面已经完成了一个完整的内部状态机,但我们并没有暴露一个方法去解析或则观察 Promise 。现在让我们开始解析 Promise

function Promise(fn) {
    ...
    doResolve(fn, resolve, reject);
}

如你所见,我们复用了 doResolve ,因为对于初始化的 fn 也要对其进行控制。 fn 允许调用 resolve 或则 reject 多次,甚至抛出异常。这完全取决于我们去保证 promise 对象仅被 resolved 或则 rejected 一次,且状态不能随意改变。

4.目前,我们已经有了一个完整的状态机,但我们仍然没有办法去观察它的任何变化。我们最终的目标是实现 then 方法,但 done方法似乎更简单,所以让我们先实现它。

我们的目标是实现 promise.done(onFullfilled, onRejected) 

  • onFulfilled 和 onRejected 两者只能有一个被执行,且执行次数为一
  • 该方法仅能被调用一次
  • 一旦调用了该方法,则 promise 链式调用结束
  • 无论是否 promise 已经被解析,都可以调用该方法
var PENDING = 0;  // 进行中
var FULFILLED = 1; // 成功
var REJECTED = 2;  // 失败

function Promise() {
  // 存储PENDING, FULFILLED或者REJECTED的状态
  var state = PENDING;

  // 存储成功或失败的结果值
  var value = null;

  // 存储成功或失败的处理程序,通过调用`.then`或者`.done`方法
  var handlers = [];

  // 成功状态变化
  function fulfill(result) {
    state = FULFILLED;
    value = result;
    handlers.forEach(handle);
    handlers = null;
  }
  // 失败状态变化
  function reject(error) {
    state = REJECTED;
    value = error;
    handlers.forEach(handle);
    handlers = null;
  }

  function resolve(result) {
    try {
      var then = getThen(result);
      if (then) {
        doResolve(then.bind(result), resolve, reject)
        return
      }
      fulfill(result);
    } catch (e) {
      reject(e);
    }
  }

  // 不同状态,进行不同的处理
  function handle(handler) {
    if (state === PENDING) {
      handlers.push(handler);
    } else {
      if (state === FULFILLED &&
        typeof handler.onFulfilled === ‘function‘) {
        handler.onFulfilled(value);
      }
      if (state === REJECTED &&
        typeof handler.onRejected === ‘function‘) {
        handler.onRejected(value);
      }
    }
  }

  this.done = function (onFulfilled, onRejected) {
    // 保证异步
    setTimeout(function () {
      handle({
        onFulfilled: onFulfilled,
        onRejected: onRejected
      });
    }, 0);
  }

  doResolve(fn, resolve, reject);
}

当 Promise 被 resolved 或者 rejected 时,我们保证 handlers 将被通知。

5.现在我们已经实现了 done 方法,下面实现 then 方法就很容易了。需要注意的是,我们要在处理程序中新建一个 Promise 

this.then = function (onFulfilled, onRejected) {
  var self = this;
  return new Promise(function (resolve, reject) {
    return self.done(function (result) {
      if (typeof onFulfilled === ‘function‘) {
        try {
          // onFulfilled方法要有返回值!
          return resolve(onFulfilled(result));
        } catch (ex) {
          return reject(ex);
        }
      } else {
        return resolve(result);
      }
    }, function (error) {
      if (typeof onRejected === ‘function‘) {
        try {
          return resolve(onRejected(error));
        } catch (ex) {
          return reject(ex);
        }
      } else {
        return reject(error);
      }
    });
  });
}

测试

完成了上面的代码,测试就很容易啦。偷个懒,测试实例来自MDN:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>promise test</title>
    <script src="http://www.chenjunxyf.me/promiseshi-xian-yuan-li/mypromise.js"></script>
</head>
<body>
    <button id="test">promise test</button>
    <div id="log"></div>
    <script>
        var promiseCount = 0;
        function testPromise() {
            var thisPromiseCount = ++promiseCount;
            var log = document.getElementById(‘log‘);
            log.insertAdjacentHTML(‘beforeend‘, thisPromiseCount + ‘) 开始(同步代码开始)‘);

            var p1 = new Promise(
                function(resolve, reject) {
                    log.insertAdjacentHTML(‘beforeend‘, thisPromiseCount + ‘) Promise开始(异步代码开始)‘);

                    window.setTimeout(function() {
                        resolve(thisPromiseCount);
                    }, Math.random() * 2000 + 1000);
                }
            );

            p1.then(
                function(val) {
                    log.insertAdjacentHTML(‘beforeend‘, val + ‘) Promise被满足了(异步代码结束)‘);
                }
            );

            log.insertAdjacentHTML(‘beforeend‘, thisPromiseCount + ‘) 建立了Promise(同步代码结束)‘);
        }

        document.querySelector(‘button‘).addEventListener(‘click‘, testPromise);
    </script>
</body>
</html>

效果:

结语

通过一份简易的实现代码,理解 Promise 原理还是挺容易的。本文所有代码请 戳这!PS:这次用了 vscode写代码,感觉非常赞!

参考

点赞

前端 javascript

时间: 2024-10-20 13:32:19

Promise实现原理的相关文章

学习Promise实现原理(附源码)

本篇文章主要在于探究 Promise 的实现原理,带领大家一步一步实现一个 Promise , 不对其用法做说明,如果读者还对Promise的用法不了解,可以查看阮一峰老师的ES6 Promise教程. 1. Promise 基本结构 new Promise((resolve, reject) => { setTimeout(() => { resolve('FULFILLED') }, 1000) }) 构造函数Promise必须接受一个函数作为参数,我们称该函数为handle,handle

promise实现原理代码

//Promise实现原理 // 实例方法 // then // catch // new P( 函数 ).then(functon(){}).then(function(){}) // let p = new MyPromise(); // p.resolve(); //不允许的 // 静态方法 // all // race // 属性 // status : pending / resolved / rejected // resolve / reject // 要求, 每个promise对

Promise核心原理解析

作者: HerryLo 本文永久有效链接: https://github.com/AttemptWeb...... Promises对象被用于表示一个异步操作的最终完成 (或失败), 及其结果值.主要是为了解决异步操作的问题. #Promise对象的状态 一个 Promise对象有以下三种状态: pending: 初始状态,既不是成功,也不是失败状态. fulfilled(resolved): 意味着操作成功完成. rejected: 意味着操作失败. Promise对象内部运行的一个变化, 变

promise的原理及面试题

const promise = new Promise((resolve, reject) => { console.log(1); resolve(); console.log(2); }) promise.then(() => { console.log(3); }) console.log(4);以上输出结果为1243首先Promise新建后立即执行,所以会先输出1,2,而Promise.then()内部的代码在当次事件循环的结尾立即执行,所以会先输出4,最后输出3. 原文地址:http

Promise 原理

剖析 Promise 之基础篇 从JavaScript的事件循环到 理解 Promise 的工作原理 Promise原理 Promise的实现原理 Promise原理解析 深入 Promise(一)--Promise 实现详解 JS Promise的实现原理 深入了解promise的原理 原文地址:https://www.cnblogs.com/yancongyang/p/8949910.html

这一次,彻底弄懂 Promise 原理

Promise 必须为以下三种状态之一:等待态(Pending).执行态(Fulfilled)和拒绝态(Rejected).一旦Promise 被 resolve 或 reject,不能再迁移至其他任何状态(即状态 immutable). 基本过程: 初始化 Promise 状态(pending)执行 then(..) 注册回调处理数组(then 方法可被同一个 promise 调用多次)立即执行 Promise 中传入的 fn 函数,将Promise 内部 resolve.reject 函数作

Javascript异步编程之三Promise: 像堆积木一样组织你的异步流程

这篇有点长,不过干货挺多,既分析promise的原理,也包含一些最佳实践,亮点在最后:) 还记得上一节讲回调函数的时候,第一件事就提到了异步函数不能用return返回值,其原因就是在return语句执行的时候异步代码还没有执行完毕,所以return的值不是期望的运算结果. Promise却恰恰要回过头来重新利用这个return语句,只不过不是返回最终运算值,而是返回一个对象,promise对象,用它来帮你进行异步流程管理. 先举个例子帮助理解.Promise对象可以想象成是工厂生产线上的一个工人

promise之nodejsQ的详细用法总结

这里主要讲node.js中Q的各种用法及说明总结,不详细介绍promise及原理. * promise是解决JS中回调层次太深 代码难懂 改起来麻烦的问题. Q是nodeJs中实现promise的包之一,是nodeJs中比较常用的一个库. 在你的项目中安装Q的方法: npm install q -save 装好后我们就可以用Q来实现nodejs的promise了! Q实现promise的常用方法有7个,在不同情况下使用不同的方法,下面就一一来介绍: ------------低-----调----

总结JavaScript编程中的Promise使用

总结JavaScript编程中的Promise使用 Promise核心说明 尽管Promise已经有自己的规范,但目前的各类Promise库,在Promise的实现细节上是有差异的,部分API甚至在意义上完全不同.但Promise的核心内容,是相通的,它就是then方法.在相关术语中,promise指的就是一个有then方法,且该方法能触发特定行为的对象或函数. Promise可以有不同的实现方式,因此Promise核心说明并不会讨论任何具体的实现代码. 先阅读Promise核心说明的意思是:看