Promise学习笔记-四(Promise实例方法)

647 阅读3分钟

实例方法then

then方法的作用

Promise的状态属性[[PromiseState]]不暴露在Promise对象上,所以不能以编程的方式检测Promise的状态,只有当Promise的状态改变时,通过then()方法采取特定的行动。接受两个参数:第一个是当Promise的状态变为fulfilled时要调用的函数,与异步操作相关的附加数据都会传递给这个完成函数(fulfillment function);第二个是当Promise的状态变为rejected时要调用的函数,所有与失败状态相关的附加数据都会传递给这个拒绝函数(rejection function)。

then方法的源码

Promise.prototype.then = function (onFulfilled, onRejected) {
    if (this.constructor !== Promise) {
        // 当前constructor不是Promise的构造器时,做些什么
        return safeThen(this, onFulfilled, onRejected)
    }
    // 定义一个空的promise对象
    var res = new Promise(noop)
    // 添加成功处理函数和失败处理函数绑定
    handle(this, new Handler(onFulfilled, onRejected, res))
    // 将promise对象return,以便then的链式调用
    return res
}
function handler(onFulfilled, onRejected, promise) {
    this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null
    this.onRejected = typeof onRejectd === 'function' ? onRejected : null
    this.promise = promise
}
function handle (self, deferred) {
    .....
    handleResolve(self, deferred)
}
function handleResolve(self, deferred) {
    // 通过当前执行状态判断cb设置为成功函数还是拒绝函数
    var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected
    if (cb === null) {
        if (self._state === 1) { // 执行成功,但没有传入成功处理函数,再次调用resolve,以便下一个串联的then方法捕获
            resolve(deferred,promise, self._value)
        } else {
            reject(deferred,promise, self._value)
        }
        return
    }
    // tryCallOne方法实现从resolve到then方法的值的穿透
    var ret = tryCallOne(cb, self._value) {
        if (ret === IS_ERROR) {
            reject(deferred,promise, LAST_ERROR)
        } else {
            resolve(deferred,promise, ret)
        }
    }
}

then的源码当中实现两个功能点:第一,then的链式调用;第二,rejectresolve输出值的穿透到then中处理。

  • then的链式调用的实现,代码如下:

    Promise.prototype.then = function (onFulfilled, onRejected) {
        ....
        var res = new Promise(noop)
        handle(this, new Handler(onFulfilled, onRejected, res))
        return res
    }
    

    首先在then的处理方法中又重新实例化了Promise对象,并且由Promise学习笔记-二一文中的源码可以看到实例化时传入的noop是一个空函数,目的就是让then返回一个Promise对象。因为每个Promise对象都必定有自己的then处理函数,所以就达到了链式调用的目的。

  • 从实例化时传入resolvereject的值,到then方法的穿透,实现代码如下:

    function tryCallOne(fn, a) {
        try {
            return fn(a)
        } catch (ex) {
            LAST_ERROR = ex
            return IS_ERROR
        }
    }
    

    then方法中传入的成功或失败处理函数和当前self._value传入到tryCallOne()方法,内部通过fn(a)实现self._value到then的穿透。

  • 链式调用时的穿透。先看一个例子

    let promise = new Promise(function (resolve, reject) {
        resolve(2)
    }).then(null).then(res => {
        console.log(res) // 2
    })
    

    第一个then方法没有传成功处理函数时,第二个then房输出了resolve中的值。在handleResolve()方法中做了什么?

    function handleResolve (self, deferred) {
        var cb = self._state === 1 ? deferred.onFulfilled ? deferred.onRejected
        if (cb === null) {
            if (self._state === 1) {
                resolve(deferred.promise, self._value)
            } else {
                reject(deferred.promise, self._value)
            }
           return
        }
    }
    

    通过handler实例我们可以知道,当第一个then传入null时,在handleResolve()方法中的cbnull,且此时当前状态值self._state又为1,所以又重新调用了resolve()方法,且return掉了。此时相当于var res = new Promise(noop)实例添加了resolve的处理方法,进而第二个then方法可以处理并得到结果2。

实例方法catch

catch()方法相当于只给其传入拒绝处理程序的then()方法。所以它的实现过程调用了实例上的then()方法。源码如下:

Promise.prototype['catch'] = function (onRejected) {
    return this.then(null, onRejected)
}

实例方法finally

finally方法的作用

finally()方法用于指定不管Promise对象状态如何,都会执行的操作。finally()的回调函数不接受任何参数,意味着没办法知道,前面的Promise的状态到底是fulfilled还是rejected。这表明finally里面的操作不依赖于Promise的执行结果。

finally用法

服务器使用Promise处理请求,然后使用finally关掉服务器

server.listen(port).then(function () {
    ....
}).finally(server.stop)

finally方法的源码

finally()本质上是then方法的特例。实现过程如下代码:

Promise.prototype.finally = function (f) {
    return this.then(function (value) {
        return Promise.resolve(f ()).then(function () {
            return value
        })
    }, function (reason) {
        return Promise.resolve(f ()).then(function () {
            throw reason
        })
    })
}

参考资料

ECMAScript 6 入门-Promise对象