promise/deferred 形式道理剖析和完成

一、什么是promise/deferred 形式

promise/deferred 形式是,依据promise/A 或许它的加强修改版promise/A+ 范例 完成的promise异步操纵的一种完成体式格局。

异步的广度运用使得回调,嵌套涌现,然则一但涌现深度的嵌套,就会让coding的体验变得相称不愉快,而且代码后期的保护也是相称费劲的。promise/deferred形式的涌现,会在肯定程度上减缓这个题目。接下来我会依据promise/a 范例来引见promise/deferred形式。
(题外话:什么是范例,范例实在就相称于制订的划定规矩,但却没有在代码层面上有默许的详细完成)

二、promise/a

promise/a 发起对单个异步操纵作出了如许的笼统定义:
1.promise操纵只会在以下3种状况中的一种:守候态(Pending)、实行态(Fulfilled)和谢绝态(Rejected)。
2.promise的状况只会涌现从守候状况向实行态或许谢绝态转化,不能够逆转。实行态和谢绝态之间不能互相转换
3.promise状况一旦被转换,就不能被变动。
《promise/deferred 形式道理剖析和完成》

4.在api上,范例定义比较简朴,只请求promise 必修提供有一个then要领,以接见当前值、最终值和谢绝缘由
then要领接收两个参数
promise.then(onFulfilled,onRejected)
5.then要领的onFulfilled,onRejected 要领都是可选参数,且不是function,都被疏忽
6.then()要领返回promise对象,以完成链式写法。

三、promise/deferred形式

promise/deferred 形式 实在包括两部分:Promise 和 Deferred。

  • Deferred主如果用于内部,来保护异步模子的状况。

  • Promise只需用于外部,经由过程then()要领,暴露给外部挪用,以增加营业逻辑和营业的组装。

promise 和 deferred的关联图
《promise/deferred 形式道理剖析和完成》

从图中能够看到:

 1.deferred对象经由过程resolve要领,转变自身状况为实行态,并触发then()要领的onfulfilled回调函数
 2.deferred对象经由过程reject要领,转变自身状况为谢绝态,并触发then()要领的onrejected回调函数

下面 我们就用代码来完成一下:

/**
 * Promise 类
 * @constructor
 */
function Promise() {
    this.handler = {};
}

/**
 * promise 对象的then要领
 * @param onFulfilled  当 promise 实行完毕后其必需被挪用,其第一个参数为 promise 的终值,其挪用次数不可凌驾一次
 * @param onRejected   当 promise 被谢绝实行后其必需被挪用,其第一个参数为 promise 的据因,其挪用次数不可凌驾一次
 * @returns {Promise}  范例定义必修返回 primise对象
 */
Promise.prototype.then = function (onFulfilled, onRejected) {
    var handler = {}
    if (typeof onFulfilled === 'function') {
        handler.resolve = onFulfilled
    }
    if (typeof onRejected === 'function') {
        handler.reject = onRejected
    }
    this.handler = handler
    return this
}

这里能够看到then要领所做的事变就是讲回调函数寄存起来,为了完成悉数流程,还须要触发实行这些回调函数的处所,而完成这些功用的对象就叫做deferred(耽误对象)。树模代码以下

function Deferred() {

    /* 状况:默许 守候态 pending */
    this.state = 'pending';

    this.promise = new Promise()
}

Deferred.prototype.resolve = function (obj) {
    this.state = 'fulfilled'
    var handler = this.promise.handler
    if (handler && handler.resolve) {
        handler.resolve(obj)
    }
}

Deferred.prototype.reject = function (obj) {
    this.state = 'rejected'
    var handler = this.promise.handler
    if (handler && handler.reject) {
        handler.reject(obj)
    }
}

以上已定义好了Promies 和Deferred ,那我们怎样对一个异步操纵函数举行封装呢?
如果我们有如许的异步函数

function asyncDoSomeing(flag, message) {
    setTimeout(function () {
        if (flag) {
            return message
        }
    }, 3000)
}

对其封装的代码就是

function asyncDoSomeing(flag, message) {
    var deferred = new Deferred()
    setTimeout(function () {
        if (flag) {
            deferred.resolve(message)
        } else {
            deferred.reject({code: 400, message: '谢绝'})
        }
    }, 3000)
    return deferred.promise
}

末了我们就能够这么运用了

asyncDoSomeing(true, '测试实行胜利').then(function (message) {
    console.log(message)
}, function (err) {
    console.log(err)
})

到这里只是单个promise对象的简朴异步的操纵掌握,然则有熟习node.js 和angular.js 的同砚就会发明,这个写法跟node.js 内里的一个异步掌握流程 q 模块(https://github.com/kriskowal/q )写法是一样的。是的哦 它就是promise/deferred 形式。随意提一下 Angularjs的$q对象是q的精简版。

四、链式挪用

 做到以上的简朴完成,抱负的coding体式格局,应当前一个挪用效果作为下一个挪用的输入,这就是链式挪用。

为了防止回调地狱,能够自创jquery的链式写法。

$('#tab').eq($(this).index()).show().siblings().hide();

链式写法的中心在于,每一个要领都返回 自身 this。

我们如今须要完成promise的链式挪用,前一个挪用效果作为下一个挪用的输入

 step1.then(step2).then(step3)

如今我们完成的then要领确实是返回this的,也就是promise自身,是能够完成链式的。
然则前一个挪用的效果却做不到是下一个挪用的输入
下面来革新一下上面的代码,让他完成这个请求。

function Promise() {
    this.handlerQueue = [];
    this.isPromise = true
}

1.将底本的handler对象改成 一个数组,寄存一切then要领的回调。

Promise.prototype.then = function (onFulfilled, onRejected) {
    var handler = {}
    if (typeof onFulfilled === 'function') {
        handler.resolve = onFulfilled
    }
    if (typeof onRejected === 'function') {
        handler.reject = onRejected
    }
    this.handlerQueue.push(handler)

    return this
}

function Deferred() {
    this.state = 'pending'
    this.promise = new Promise()
}

Deferred.prototype.resolve = function (obj) {
    this.state = 'fulfilled'
    var promise = this.promise
    var handler = {}
    while (handler = promise.handlerQueue.shift()) {
        if (handler && handler.resolve) {
            var res = handler.resolve(obj)
            if (res && res.isPromise) {
                res.handlerQueue = promise.handlerQueue
                this.promise = res
                return;
            } else {
                obj = res
            }
        }
    }
}

Deferred.prototype.reject = function (obj) {
    this.state = 'rejected'
    var promise = this.promise
    var handler = {}
    while (handler = promise.handlerQueue.shift()) {
        if (handler && handler.reject) {
            var res = handler.reject(obj)
            if (res && res.isPromise) {
                res.handlerQueue = promise.handlerQueue
                this.promise = res
                return;
            } else {
                obj = res
            }
        }
    }
}

//------ test-------//
function asyncDosomeing(flag, name) {
    const deferred = new Deferred()
    setTimeout(function () {
        if (flag) {
            deferred.resolve({code: 200, message: '胜利', name: name})
        } else {
            deferred.reject({code: 400, message: '失利', name: name})
        }
    }, 2000)
    return deferred.promise
}
asyncDosomeing(true, 'asyncDosomeing1').then(result => {
    console.info(result)
    return asyncDosomeing(false, 'asyncDosomeing2')
}).then(result => {
    console.info(result)
    return 'dadds'
}).then(result => {
    console.info(result)
})

五、一致的非常处置惩罚(谢绝处置惩罚)

那如今,我们有个需求,想完成一切的谢绝一致在一个处所处置惩罚。而不是每一个then要领都传一个rejected 回调,只愿望then()要领能够,安安心心的处置惩罚胜利的回调。

 step1().then(step2).then(step3).catch(function(err){
// do something when err
})

加一个catch err 的回调,当涌现非常就直接到这个流程上处置惩罚。
那我们就在promise 的原型上架一个catch要领,以下

Promise.prototype.catch = function (onRejected) {

var handler = {}
if (typeof onRejected === 'function') {
    handler.reject = onRejected
}
this.handlerQueue.push(handler)
return this
}

//------ test-------//
function asyncDosomeing(flag, name) {
    const deferred = new Deferred()
    setTimeout(function () {
        if (flag) {
            deferred.resolve({code: 200, message: '胜利', name: name})
        } else {
            deferred.reject({code: 400, message: '失利', name: name})
        }
    }, 2000)
    return deferred.promise
}
asyncDosomeing(true, 'asyncDosomeing1').then(result => {
    console.info(result)
    return asyncDosomeing(false, 'asyncDosomeing2')
}).then(result => {
    console.info(result)
    return 'dadds'
}).then(result => {
    console.info(result)
}).catch(err => {
    console.info('catch')
    console.info(err)
    return asyncDosomeing(true, 'asyncDosomeing3----catch')
})

如许就能够完成,只需异步操纵流程中有一步被谢绝,下面流程就天然中缀,直接到catch回调中处置惩罚非常。

六、API Promise化

在编码的时刻,想要用promise举行异步操纵流程掌握,就要将当前的异步回调函数封装成promise。在本身开辟的时刻,每每会去援用第三方的模块,然后发明这些模块的异步回调API 不支持promise写法。岂非我们本身悉数封装完成一遍?!这显著是不合理的。那我们就能够完成一个 要领能够批量将要领Promise化,相干代码以下:

1.在deferred原型上完成一个异步回调函数,回调实行后触发deferred resolve 和 reject的要领

Deferred.prototype.callBack = function () {
    var that = this
    return function (err, result) {
        if (err) {
            that.reject(err)
        } else {
            that.resolve(result)
        }
    }
}

2.定义一个Api Promise化 要领

/**
 * 将异步操纵转换成promise
 */
var promisify = function (method) {
    if (typeof method !== 'function') {
        throw new TypeError('is not a function')
    }
    return function () {
        const defrred = new Deferred()
        var args = Array.prototype.slice.call(arguments, 0) // 克隆参数
        args.push(defrred.callBack())
        method.apply(this, args)
        return defrred.promise
    }
}

末了我们就能够简化代码

var readFile = promisify(fs.readFile);
readFile('file.text').then(function(file){
    return readFile(file.trim())
}).then(function(file2){
    console.log(file2)
})

这里只是对promise/deferred 道理的简朴完成,另有许多状况没有斟酌。愿望人人在做promise异步流程操纵的时刻,照样挑选如今成熟的模块。比方 q模块、bulebird、when、或许 es6 的promise 去做。

    原文作者:lyf87
    原文地址: https://segmentfault.com/a/1190000010167155
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞