简单来说,promise就是一个承诺,承诺本身会做出正确延时或异步操作。承诺会解决callback处理异步回调可能产生的调用过早,过晚或调用次数过多过少、吞掉可能出现的错误或异常问题等。另外,承诺只接受首次resolve()或reject()决议,承诺本省状态改变后就不会再改变,承诺所有通过then()注册的回调总是一次异步调用,承诺所有异常总会被捕获抛出。
严谨地讲,promise是一种封装和组合未来值得易于复用的机制,实现关注点分离、异步流程控制、异常冒泡、串行|并行控制等。
promise的状态必须为以下三种状态之一:等待态(pengding)、执行态(resolved)和拒绝态(rejected)。等待态可以向执行态或拒绝态迁移,一旦改变将一直维持这个状态。
首先,我们看看promise构造函数需要做什么事情。
1.初始化Promise状态为pending。 2.初始化then()注册回调函数数组(then()方法可被同一个promise调用多次) 3.立即执行传入的fn函数,传入Promise内部resolve、reject函数。
function Promise(fn) {
// 初始化promise状态
// 0-pengding
// 1-resolved
// 2-rejected
this._state = 0;
// promise执行结果
this._value = null;
// 注册回调处理数组
this.deferreds = [];
// 立即执行fn函数
try {
fn(value => {
resolve(this, value);
}, reason => {
reject(this, reason);
})
} catch(err) {
reject(this, err);
}
}
then方法可以被同一个promise调用多次,每次返回新promise对象。then方法接受两个参数onResolved、onRejected(可选)。在promise被resolve或reject后,所有onResolved或onRejected函数须按照其注册顺序依次回调,且调用次数不超过一次。
那么,then函数的执行流程大致为:
1.实例化空promise对象用来返回,用以保持链式调用。 2.构造then()注册回调处理函数体。 3.判断当前promise状态,pending状态存储延迟处理对象deferred,非pending状态执行onResolved或onRejected回调。
链式调用为什么返回新的promise对象?
因为当前的promise对象的状态已经转移,无法再改变,会影响后面的回调。
promise实例化时立即执行传入的fn函数,同时传递内部resolve函数作为参数用来改变promise状态。resolve函数的执行逻辑大概为:判断并改变当前promise状态,存储resolve()的value值。判断当前是否存在then()注册回调函数,若存在则依次异步执行onResolved回调。
为使Promise的实现更具有通用性,当value为存在then()方法的thenable对象,需要做Promise Resolution Procedure处理,规范描述为 [[Resolve]](promise, x),x即为后面value参数。
具体的处理逻辑如下:
如果promise和x指向同一对象,以TypeError为据拒绝执行promise。
如果x为promise,则使promise接受x的状态。
1.把x.then赋值给then。 2.如果取x.then的值时抛出错误e,则以e为据拒绝promise。 3.如果then是函数,将x作为函数的作用域this调用之。 4.如果x不是对象或者函数,以x为参数执行promise。
resolve函数逻辑较为复杂,主要是集中处理value值的多种可能。如果value为promise且状态为pending时,须使promise接受value的状态。在value状态为pending时,简单将promise的deferreds回调处理数组赋予value.deferreds变量。非pending状态,使用value内部值回调promise注册的deferreds。
如果value为thenable对象,以value作为函数的作用域this调用之,同时回调调用内部resolve()、reject()函数。
其他情形则以value为参数执行promise,调用onResolved和onRejected处理函数。
promise内部的私有方法reject相较于resolve逻辑就简单多了,如下所示:
6.handleResolved函数
在此之前,我们多次调用了handleResolved函数却还没实现,那么此函数到底做了哪些事情呢?
handleResolved函数具体会根据promise当前状态判断调用onResolved、onRejected,处理then()注册回调为空的情形,以及维护链式then()函数后续调用。具体实现如下:
function handleResolved(promise, deferred) { // 异步注册回调 asyncFn(function() { var cb = promise._state === 1? deferred.onResolved: deferred.onRejected;
}
具体处理注册回调函数cb为空情形,如下面实例。判断当前回调cb为空时,使用deferred.promise作为当前promise,结合value调用后续处理函数继续往后执行,实现值穿透空处理函数往后传递。
实现then函数的链式调用,只需要在Promise.prototype.then()处理函数中返回新的promise实例即可。但除此之外,还需要依次调用then注册的回调处理函数。如handleResolved函数最后一句resolve(deferred.promise,res)所示。
7.基于Promise,原生js实现ajax
XMLHttpRequest的readyState属性表示这个请求所处的状态:
send()方法已经被调用,响应头和响应状态已经返回
解读Promise内部实现原理
Promises/A+
剖析Promise内部结构,一步一步实现一个完整的、能通过所有Test case的Promise类
使用Promise封装简单Ajax方法