Promise对目前的前端开发者来说已经是必须去了解甚至掌握的一个东西了,ES6规范中甚至定义了原生的Promise对象,对于它到底是什么的问题请参考这篇文章Promise基础
因为最初Promise的概念提出的时候,一群人为了规范它,产生了一个叫Promise A的规范,而本文要说的是Promise A+,没错多了一个加号,说明是有不一样的,详细的规范细节请点击Promise/A+,该规范是对 Promise/A 规范的补充和修改,它出现的目的是为了统一异步编程中的接口,本文会就结合规范来一步步实现Promise A+代码
Promise实现的几个重点
- 要保证then方法回调的时序,即第一个promise兑现的时候,后面链式调用的then方法参数回调依次调用,但同时这些方法又不能在执行then方法的时候立即执行,必须等待前面promise兑现
第一个promise的兑现是在2秒后,2秒后我才能执行fn1函数,接着执行fn2函数,但是2秒内我的then方法就已经结束返回了。解决办法很简单,引入一个函数数组队列来保存then方法的参数fn1,fn2,2秒之后再来执行。
var promise = new Promise(function (resolve, reject) {
setTimeout(function(){
resolve(100);
},2000)
})
promise.then(function fn1(val){
return val+100
}).then(function fn2(val){//这里为了便于说明给匿名函数加了个名字
alert(val);
})
- 如果then方法执行的时候前面的promise已经兑现了,那后面链式调用then方法的回调如fn3应该立即执行,那如何知道它已经兑现了?这也是为什么会定义promise的状态.
var promise = new Promise(function (resolve, reject) {
setTimeout(function(){
resolve(100);
},2000)
})
promise.then(function fn1(val){
return val+100
}).then(function fn2(val){//这里为了便于说明给匿名函数加了个名字
alert(val);
}).then(function fn3(val){
alert(val);
}
- promise A+规范规定所有的then的2个参数的回调都应该是异步的,必须在then方法返回之后异步执行,对于我们实现来说就是必须把回调加入到下一个函数队列循环中,常用的有setTimeout,node中可以用process.nextTick,后面实现会说明
var promise = new Promise(function (resolve, reject) {
setTimeout(function(){
resolve(100);
},2000)
})
//这里fn1必须在then方法返回之后才能异步调用
var promise1=promise.then(function fn1(val){
return val+100
})
- 关于规范中
promise2 = promise1.then(onFulfilled, onRejected);
仔细看规范知道,这里可能会有3个promise即promise1
,onFulfilled返回的thenpromise
,promise2
,promise2必须在thenpromise兑现之后才能reslove
promise的几种状态
pending
表示正在发生中fulfilled
表示已经履行rejected
表示被拒绝
var State = {
PENDING: 0,
FULFILLED: 1,
REJECTED: 2
};
then方法
then
方法必须同样返回一个promise对象,实现如下,请对照Promise A+规范,注释中的数字会有标识
function then(onFulfilled, onRejected) {
var self = this;
var promise = new Promise(function () {
});
self._subscribers.push({//2.2.6
fulfillPromise: typeof onFulfilled == "function" ? onFulfilled : null,//2.2.1,2.2.5
rejectPromise: typeof onRejected == "function" ? onRejected : null,//2.2.1,2.2.5
thenPromise: promise
})
invokeCallback(self);
return promise; //2.2.7
}
invokeCallback方法
用于处理promise兑现之后的回调队列
function invokeCallback(promise) {
if (promise._state === State.PENDING) {
return;
}
async(function () {//2.2.4
while (promise._subscribers.length) {
var obj = promise._subscribers.shift();//2.2.6
try {
//先把当前promise的值作为参数传递给fulfillPromise,然后后面根据返回的value值来作为兑现thenPromise的参数,这里是保证调用顺序的基础
var value = (promise._state === State.FULFILLED ?
(obj.fulfillPromise || function (x) {
return x;//2.2.7.3
}) :
(obj.rejectPromise || function (x) {
throw x;//2.2.7.4
}))
(promise._value);//2.2.2,2.2.3
} catch (e) {
reject(obj.thenPromise, e);//2.2.7.2
continue;
}
resolve(obj.thenPromise, value);//2.2.7.1
}
})
}
//3.1 依赖于执行环境
function async(fn) {
setTimeout(fn, 0);
}
[[Resolve]](promise, x)
方法
resolve作为规范里面严格定义的一个过程,实现如下
function resolve(promise, x) {
if (promise === x) {//2.3.1
reject(promise, new TypeError("it cant't fulfill promise equals value condition"));
}
else if (x && x.constructor === Promise) {//2.3.2
if (x._state === State.PENDING) {//2.3.2.1
x.then(function (val) {
resolve(promise, val)
}, function (reason) {
reject(promise, reason);
})
}
else if (x._state === State.FULFILLED) {//2.3.2.2
fulfill(promise, x._value);
}
else if (x._state === State.REJECTED) {//2.3.2.3
reject(promise, x._value);
}
}
else if (x !== null && (typeof x == "object" || typeof x == "function")) {
var isCalled = false;//2.3.3.3.3
try {
var then = x.then;//2.3.3.1
if (typeof then == "function") {//2.3.3.3
then.call(x, function (val) {
isCalled || resolve(promise, val);//2.3.3.3.1
isCalled = true;
}, function (reason) {
isCalled || reject(promise, reason);//2.3.3.3.2
isCalled = true;
})
}
else {
fulfill(promise, x);//2.3.3.4
}
} catch (e) {
isCalled || reject(promise, e);//2.3.3.2 ,2.3.3.3.4
}
}
else {
fulfill(promise, x);//2.3.4
}
}
Promise A+与Promise A的主要区别
A+规范强调了不同实现之间的互操作和混用,通过术语thenable来区分promise对象,当一个对象拥有then函数就认为是promise对象,使用鸭子类型判断来允许不同实现之前的兼容和互相操作
A+定义当
onFulfilled
或者onRejected
返回promise时后的处理过程,他们必须是作为函数来调用,而且调用过程必须是异步的A+严格定义了
then
方法链式调用时onFulfilled
或者onRejected
的调用顺序
插曲
为什么jquery不是标准的promise实现 该文的作者说明了为什么jquery promise不是一个符合规范的实现,主要观点是认为jquery的then方法返回的promise不是一个新的promise,而只是去修改当前promise的状态,但规范严格规定了状态之间的过渡是不可逆的