再谈Promise

之前写了一篇关于ES6原生Promise的文章。近期又读朴灵的《深入浅出Node》,内里引见了一个Promise/Deferred情势。

Promise是处置惩罚异步题目的利器。它实际上是一种情势。Promise有三种状况,未完成态、完成态、失利态,置信人人肯定不生疏,Promise对象许可运用.then的情势,将回调放到IO操纵等异步要领的主体以外,使代码幽美不少。

下面我连系《深入浅出Node》,引见一下怎样用ES5完成Promise/Deferred情势。置信研讨完该完成代码以后,我们会对Promise的邃晓更进一步。

Promise

then要领完成回调注册

Promise/Deferred情势下,Promise对象经由过程then要领挪用,注册完成态和失利态的回调函数。

由于then要领支撑链式回调,因而then要领的返回值肯定也是Promise对象,我们在此简朴的返回本身,也就是this

那末肯定有人要问了:then中的回调函数,能够返回一个新的Promise对象,今后的then挪用是不是是在新的Promise对象上挪用的呢?

答案是:不肯定

这个题目实在搅扰我良久,直到看了Promise的完成代码我才想邃晓。实在then要领的挪用,只不过是注册了完成态和失利态下的回调函数罢了。这些回调函数构成一个回调行列,处置惩罚resolve的值。

Promise组织函数注册回调行列

Promise组织函数,给每一个实例一个queue属性,将then要领注册的回调行列,保存在Promise实例的回调行列中。

代码

var Promise = function(){
    this.queue = [];
} 

Promise.prototype.then = function(fulfilledHandler, unfulfilledHandler){
    var handler = {};
    if (typeof fulfilledHandler === "function"){
        handler.fulfilled = fulfilledHandler;
    }
    if (typeof unfulfilledHandler === "function"){
        handler.unfulfilled = unfulfilledHandler;
    }
    this.queue.push(handler);
    return this;
}

我们看到,Promise的代码很简朴,只是经由过程then要领将一系列的回调函数push到行列中罢了。Promise实例暴露给用户的也只要一个then要领。

如许我们就能够够如许挪用了:

promise.then(fulfilledFunc1, unfulfilledFunc1)
    .then(fulfilledFunc2, unfulfilledFunc2)
    .then(fulfilledFunc3, unfulfilledFunc3)

那末怎样举行状况转换呢?下面我就来说一下带有resolve要领(reject要领同理,下面均以resolve举例)的Deferred。

Deferred

Deferred实例决议Promise实例的状况

每一个Deferred实例的对应一个Promise实例。挪用Deferred实例的resolve要领,能使Promise注册的回调行列中的回调函数顺次实行。

先写部份代码:

var Deferred = function(){
    this.promise = new Promise();
}

Deferred.protoype.resolve = function(val){
    var handler, value = val;
    while(handler = this.promise.queue.shift()){
        if (handler && handler.fulfilled){
            value = handler.fulfiller(value) && value;
        }
    }
}

如许我们就能够运用Deferred实例返回Promise实例,而且运用Deferred实例的resolve要领来触发Promise实例的完成态回调,而且将上一个回调如果有返回值,我们将该返回值作为新的resolve值传递给背面的回调。

处置惩罚回调要领返回的Promise实例

依据Promise情势,回调函数返回Promise实例时,下一个then()中的回调处置惩罚的是新的Promise实例。

在之前的代码完成中,then要领注册了一系列的回调函数,这些回调函数应当处置惩罚新的promise实例。这里我们用了一个小技能,见代码:

Deferred.protoype.resolve = function(val){
    var handler, value = val;
    while(handler = this.promise.queue.shift()){
        if (handler && handler.fulfilled){
            value = handler.fulfiller(value) && value;
            // 修正的地方在这里:
            if (value && value.isPromise){
                value.queue = this.promise.queue;
                // 末了再加一个小技能
                this.promise = value;
                
                return;
            }
        }
    }
}

我们将返回promise实例以后的回调列表一成不变的注册到返回的promise中,如许就保证之前then注册的回调行列能继承挪用。末了的小技能能够使旧的deferred实例对应新的promise实例,如许能够继承运用deferred.resolve要领。

为了推断实例是不是是Promise实例,这里简朴的修正Promise组织函数:

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

封装callback要领用于异步挪用

Promise之所以是处置惩罚异步的利器,一方面是then要领的链式挪用,一方面也是由于resolve要领能够异步挪用,触发还调行列。

由于以NodeJS为标志的异步要领其回调函数类似于如许:

asyncFunction(param, function(err, data){
    // do something...
});

我们能够封装一个本身的callback要领,用于异步触发resolve要领。

Deferred.prototype.callback = function(err, data){
    if (err){
        this.reject(err);
    }
    this.resolve(data);
}    

今后我们能够如许promisify一个异步函数:

var async = function(param){
    var defer = new Deferred();
    var args = Array.prototype.silce.call(arguments);
    args.push(defer.callback);
    asyncFunc.apply(null, args);
    return defer.promise;
}

Promisify

由上面的promisify思绪,我们写一个更一般化的promisify函数:

var promisify = function(method){
    return function(){
        var defer = new Deferred();
        var args = Array.prototype.silce.call(arguments, 1);
        args.push(defer.callback);
        asyncFunc.apply(null, args);
        return defer.promise;
    }
}

举一个Node中文件操纵的例子:

readFile = promisify(fs.readFile);

readFile('file1.txt', 'utf8').then(function(file1){
    return readFile(file1.trim(), 'utf8');
}).then(function(file2){
    console.log(file2);
})

俩字:文雅。

完毕

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