用了这么长时间的promise,也看了很多关于promise 的文章博客,对promise 算是些了解。但是要更深的理解promise,最好的办法还是自己实现一个。

我大概清楚promise 是对异步概念的包装,当你拿到一个promise 对象,你并不是拿到你想要的值,而只是这个值的一个“承诺”。这个承诺可能被实现,从而你可以拿到最终想要的值,但也可能被拒绝,然后得到原因。关于promise 对编程风格的改善可以网上有很多文章可以参考,比如这篇。

var promise = new Promise(function (resolve, reject) {
setTimeout(function(){
resolve(1);
}, 1000);
}).then(function(n) {
var p = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(n + 2);
}, 2000);
})
return p;
}).then(function(r) {
console.log(r);
})

上面是一个使用promise 的例子,整个流程需要经过两个异步操作的处理。第一个异步操作是要花费1秒得到整数1,紧接着花费2秒将前一个操作的返回值加上2。

最后等待所有操作完成后将结果打印。 1 —> 1+2 —> log(3)

既然是异步操作,你怎么能保证then 过去的函数一定被调用呢?如果还没等then 执行就resolve 了,这样怎么保证回调依然被执行的?答案就是:其实promise 是个状态机,初试状态是pending,当异步操作完成后变为resolved,或者被拒绝变为rejected。那么每当我then 的时候,如果是pending 状态就把这个回调存起来,如果是resolve 状态就立即执行这个回调。

为了能一直then 下去,then必须返回一个promise,这样我们可以构造出一系列then 链条,链条上的每个promise 都观察着前一个promise,一旦前一个promise 被实现,后续的一个promise 立即被执行,如此传递下去。当然如果其中一环被拒绝的话,整个链条就断了,后续不再执行。

Version 1:

var PENDING = 0,
RESOLVED = 1,
REJECTED = 2; function Promise(fn) {
var state = PENDING;
var value;
var callback; var doResolve = function(_value) {
if (state === PENDING) {
value = _value;
state = RESOLVED;
if (callback)
callback(value);
} else {
throw new Error("A promise can only been resolved once.");
}
}var doReject = function(_reason) {
state = REJECTED;
throw _reason;
} this.then = function (_callback) {
return new Promise(function (_resolve, _reject) {
var dummy_callback = function (_value) {
_resolve(_callback(_value));
}
if (state === PENDING) {
callback = dummy_callback
} else {
dummy_callback(value);
}
});
} fn(doResolve, doReject);
}

上面的代码实现了一个简单的promise, 但是有一个很严重的问题,无法处理返回promise 的情况(文章开头的那个例子),如果其中一个promise 返回的是另一个promise,那么我们应该把这个新的promise 纳入链条,等待其resolve 后再继续执行剩下的链条。

Version2:

var PENDING = 0,
RESOLVED = 1,
REJECTED = 2; function Promise(fn) {
var state = PENDING;
var value;
var callback; var doResolve = function(_value) {
if (state === PENDING) {
value = _value;
state = RESOLVED;
if (callback)
callback(value);
} else {
throw new Error("A promise can only been resolved once.");
}
} var real_resolve = function (_value) {
if (_value && typeof _value.then === "function") {
_value.then(doResolve);
} else {
doResolve(_value);
}
} var doReject = function(_reason) {
state = REJECTED;
throw _reason;
} this.then = function (_callback) {
return new Promise(function (_resolve, _reject) {
var dummy_callback = function (_value) {
_resolve(_callback(_value));
}
if (state === PENDING) {
callback = dummy_callback
} else {
dummy_callback(value);
}
});
} fn(real_resolve, doReject);
}

我在doResolve 之前加了一个real_resolve 用来处理万一reolsve一个promise 的情况,这里投了一个懒,直接调用了value.then 创建一个新的promise 加入链条。但是本质上可以用value本身。虽然有待改进,但是目的已经达到了,这就是一个简单的promise 实现。

参考:https://www.promisejs.org/implementing/

   https://github.com/kriskowal/q/blob/v1/design/README.js

一个简单的Promise 实现的更多相关文章

  1. 如何用原生JS实现一个简单的promise

    我又又又回来了,最近真是累的跟狗一样,急需一个大保健回复一下子精力 我现在是一边喝着红牛一边写着博客,好了好了,不扯了,回归整体好吧 先简单来说一下啥是promise吧 它是什么?Promise是一个 ...

  2. [手写系列] 带你实现一个简单的Promise

    简介 学习之前 需要先对Promise有个基本了解哦,这里都默认大家都是比较熟悉Promise的 本次将带小伙伴们实现Promise的基本功能 Promise的基本骨架 Promise的then Pr ...

  3. 实现简单的promise

    只考虑成功时的调用,方便理解一下promise的原理promise的例子: 1. 接下来一步步实现一个简单的promise step1:promise 接受一个函数作为构造函数的参数,是立即执行的,并 ...

  4. 【JavaScript进阶】深入理解JavaScript中ES6的Promise的作用并实现一个自己的Promise

    1.Promise的基本使用 // 需求分析: 封装一个方法用于读取文件路径,返回文件内容 const fs = require('fs'); const path = require('path') ...

  5. 简单版 Promise/A+,通过官方872个测试用例

    promise 标准 在实现 Promise 之前要清楚的是 JavaScript 中的 Promise 遵循了 Promises/A+ 规范,所以我们在编写 Promise 时也应当遵循这个规范,建 ...

  6. 一个简单的例子搞懂ES6之Promise

    ES5中实现异步的常见方式不外乎以下几种: 1. 回调函数 2. 事件驱动 2. 自定义事件(根本上原理同事件驱动相同) 而ES6中的Promise的出现就使得异步变得非常简单.promise中的异步 ...

  7. 手把手教你实现一个完整的 Promise

    用过 Promise,但是总是有点似懂非懂的感觉,也看过很多文章,还是搞不懂 Promise的 实现原理,后面自己边看文章,边调试代码,终于慢慢的有感觉了,下面就按自己的理解来实现一个 Promise ...

  8. 实现一个自己的promise

    这是小弟的一篇开篇小作,如有不当之处,请各位道友批评指正.本文将探讨Promise的实现. 一.ES6中的Promise 1.简介 据说js很早就实现了Promise,我是不知道的,我第一次接触Pro ...

  9. koa2源码解读及实现一个简单的koa2框架

    阅读目录 一:封装node http server. 创建koa类构造函数. 二:构造request.response.及 context 对象. 三:中间件机制的实现. 四:错误捕获和错误处理. k ...

随机推荐

  1. BZOJ 1511: [POI2006]OKR-Periods of Words

    Description 求一个最长周期. Sol KMP. 一个点的最短周期就是 \(i-next[i]\) 此外 \(i-next[next[i]],i-next[next[next[i]]]\) ...

  2. ubuntu15.10下编译安装wine1.8 rc4

    ubuntu15.10下编译安装wine1.8rc4 Wine (“Wine Is Not an Emulator” 的递归缩写)是一个能够在多种 POSIX-compliant 操作系统(诸如 Li ...

  3. Redis常用数据类型介绍、使用场景及其操作命令

    Redis常用数据类型介绍.使用场景及其操作命令 本文章同时也在cpper.info发布. Redis目前支持5种数据类型,分别是: 1.String(字符串) 2.List(列表) 3.Hash(字 ...

  4. POCO浅探

    POCO作用类似于POJO.POJO的内在含义是指那些从来没有任何类继承.也没有实现任何接口,更是没有被其他框架侵入的JAVA对象.POCO的含义与此是一致的.POJO的意义就在于它的简单灵活性,能够 ...

  5. java接口和抽象类

    关于接口 1.创建一个接口,需要使用interface关键字. 2.实现一个接口,需要使用implements关键字. 3.接口的成员属性都是静态常量(默认public static final). ...

  6. Redis集群的部署

    Redis集群分为主节点Master和从节点Slave,主节点只有1个,而从节点可以有多个,这样从节点和主节点可以进行数据的传输,Redis集群的性能将比单机环境更高,接下来是配置的过程 首先配置Ma ...

  7. selenium webdriver学习(一)

    package baidu; import java.io.File; import java.io.IOException; import junit.framework.TestCase; imp ...

  8. (Win7 x64)NetBeans 8.0.2 使用Tomcat 8作为服务器

    1.下载Apache Tomcat,解压至本地硬盘的根目录. 2.运行CMD,输入: 解压盘符:\apache-tomcat-8.0.xx\bin\service.bat install 3.安装完成 ...

  9. “假学习“&”真学习“?(摘)

    什么叫做“假学习”? 一.看书 买一堆书,有空看看.看书,这是典型的假学习.看书看不懂还在看,就是假学习,欺骗自己,安慰自己正在学习而已.专业书都写得很好,但大都是写给已经懂的人看的.看书的最大作用就 ...

  10. jquery.base64.js简单使用

    jquery.base64.js, 加密,使用,先引入jquery,然后引入jquery.base64.js 使用如下 js中加密如下 $.base64.encode(result[i].ipadre ...