Promise
对于一个Jser并不陌生, Promise 类似于一个事务管理器,它的作用就是将各种内嵌回调的事务用流水形式表达。利用 Promise 可以让异步编程更符合人的直觉,让代码逻辑更加清晰,把开发人员从回调地狱中释放出来。这么“高大上”的东西,以前写 nodejs 代码的时候只是简单的用用,还没有理解其基本的实现原理,罪过!
个人认为,理解编程思想最好的途径就是阅读一份简易的实现源码。很幸运,网上有不少Promise的简易实现,下面就来好好研究下吧!
基础概念
目前, Promise
是ECMAScript 6
规范的重要特性之一,各大浏览器也开始慢慢支持这一特性。当然,也有一些第三方内库实现了该功能,最流行的如:bluebird等。Promise
对象用来进行延迟( deferred )和异步( asynchronous )计算。一个Promise 处于以下四种状态之一:
- pending: 还没有得到肯定或者失败结果,进行中
- fulfilled: 成功的操作
- rejected: 失败的操作
- settled: 已被 fulfilled 或 rejected
Promise 对象有两个重要的方法,一个是 then
,另一个是 resolve
:
- then:将事务添加到事务队列中
- resolve:开启流程,让整个操作从第一个事务开始执行
Promise 常用方式如下:
示意图如下:
实现步骤
Promise 其实就是一个状态机。按照它的定义,可从如下基础代码开始:
123456789101112131415161718192021var 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;}}下面是 Promise 的 resolve 方法实现:
注意:resolve
方法可接收的参数有两种:一个普通的值/对象或者一个Promise
对象。如果是普通的值/对象,则直接把结果传递到下一个对象;
如果是一个Promise
对象,则必须先等待这个子任务序列完成。1234567891011121314151617function 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);}}}两个辅助方法:
12345678910111213141516171819202122232425262728293031323334353637383940/*** 检查值是否是一个Promise,如果是,返回这个Promise的 `then`方法。* @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;}/*** 确保onfulfilled和onrejected只调用一次。** @param {Function} fn* @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);}}上面已经完成了一个完整的内部状态机,但我们并没有暴露一个方法去解析或则观察
Promise
。现在让我们开始解析Promise
:1234function Promise(fn) {...doResolve(fn, resolve, reject);}如你所见,我们复用了
doResolve
,因为对于初始化的 fn 也要对其进行控制。fn 允许调用 resolve 或则 reject 多次,甚至抛出异常。
这完全取决于我们去保证 promise 对象仅被 resolved 或则 rejected 一次,且状态不能随意改变。目前,我们已经有了一个完整的状态机,但我们仍然没有办法去观察它的任何变化。我们最终的目标是实现
then
方法,但done
方法似乎更简单,所以先实现它。
我们的目标是实现promise.done(onFullfilled, onRejected) :
- onFulfilled 和 onRejected 两者只能有一个被执行,且执行次数为一次
- 该方法仅能被调用一次
- 一旦调用了该方法,则 promise 链式调用结束
- 无论是否 promise 已经被解析,都可以调用该方法
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869var 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 shandle(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
将被通知。- 现在我们已经实现了
done
方法,下面实现then
方法就很容易了。需要注意的是,要在处理程序中新建一个Promise
。123456789101112131415161718192021222324252627this.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:
效果:
结语
通过一份简易的实现代码,理解Promise
原理还是挺容易的。全部代码 !