Promise原理及实现

简单来说,promise就是一个承诺,承诺本身会做出正确延时或异步操作。承诺会解决callback处理异步回调可能产生的调用过早,过晚或调用次数过多过少、吞掉可能出现的错误或异常问题等。另外,承诺只接受首次resolve()reject()决议,承诺本省状态改变后就不会再改变,承诺所有通过then()注册的回调总是一次异步调用,承诺所有异常总会被捕获抛出。

严谨地讲,promise是一种封装和组合未来值得易于复用的机制,实现关注点分离、异步流程控制、异常冒泡、串行|并行控制等。

1.Promise状态

promise的状态必须为以下三种状态之一:等待态(pengding)、执行态(resolved)和拒绝态(rejected)。等待态可以向执行态或拒绝态迁移,一旦改变将一直维持这个状态。

2.Promise构造函数

首先,我们看看promise构造函数需要做什么事情。

1.初始化Promise状态为pending。 2.初始化then()注册回调函数数组(then()方法可被同一个promise调用多次) 3.立即执行传入的fn函数,传入Promise内部resolvereject函数。

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);
    }
}

3.then函数

then方法可以被同一个promise调用多次,每次返回新promise对象。then方法接受两个参数onResolvedonRejected(可选)。在promiseresolvereject后,所有onResolvedonRejected函数须按照其注册顺序依次回调,且调用次数不超过一次。

那么,then函数的执行流程大致为:

1.实例化空promise对象用来返回,用以保持链式调用。 2.构造then()注册回调处理函数体。 3.判断当前promise状态,pending状态存储延迟处理对象deferred,非pending状态执行onResolvedonRejected回调。

链式调用为什么返回新的promise对象?

因为当前的promise对象的状态已经转移,无法再改变,会影响后面的回调。

4.resolve函数

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

  • 如果xpromise,则使promise接受x的状态。

  • 如果x为对象或函数:

1.把x.then赋值给then。 2.如果取x.then的值时抛出错误e,则以e为据拒绝promise。 3.如果then是函数,将x作为函数的作用域this调用之。 4.如果x不是对象或者函数,以x为参数执行promise

resolve函数逻辑较为复杂,主要是集中处理value值的多种可能。如果valuepromise且状态为pending时,须使promise接受value的状态。在value状态为pending时,简单将promisedeferreds回调处理数组赋予value.deferreds变量。非pending状态,使用value内部值回调promise注册的deferreds

如果valuethenable对象,以value作为函数的作用域this调用之,同时回调调用内部resolve()reject()函数。

其他情形则以value为参数执行promise,调用onResolvedonRejected处理函数。

5.reject函数

promise内部的私有方法reject相较于resolve逻辑就简单多了,如下所示:

6.handleResolved函数

在此之前,我们多次调用了handleResolved函数却还没实现,那么此函数到底做了哪些事情呢?

handleResolved函数具体会根据promise当前状态判断调用onResolvedonRejected,处理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

XMLHttpRequestreadyState属性表示这个请求所处的状态:

状态

描述

0

UNSENT

open()方法还未被调用

1

OPENED

open()方法已经被调用

2

HEADERS_RECEIVED

send()方法已经被调用,响应头和响应状态已经返回

3

LOADING

正在下载响应体

4

DONE

请求过程已经完毕

1.实现get方法

2.实现post方法

8.参考文献

解读Promise内部实现原理arrow-up-right

Promises/A+arrow-up-right

剖析Promise内部结构,一步一步实现一个完整的、能通过所有Test case的Promise类arrow-up-right

使用Promise封装简单Ajax方法arrow-up-right

Last updated