build your promise step by step

近来看了一篇关于Promise内部完成道理的文章Javascript in wicked detail。作者从简明的例子入手,一步一步的构建硬朗的Promise完成。我就拿作者文中的代码实例梳理下文章的核心内容。

人人肯定看到过嵌套很深回调函数,那末如安在保证代码流程才将这些纵向嵌套的代码变成横向偏平的呢?

    doSomething(function(value) {
        console.log('Got a value:' + value);
    })

to this

    doSomething().then(function(value) {
        console.log('Got a value:' + value);
    })

那末我们就应该在定义doSomething函数的时刻做出响应的变化

    function doSomething(callback) {
        var value = 42;
        callback(value);
    }

to this

    function doSomething() {
        return {
            then: function(callback) {
                var value = 42;
                callback(42);
            }
        }
    }

Defining the Promise type

起首来看一段定义简朴Promise组织函数的代码:

    function Promise(fn) {
        var callback = null;
        this.then = function(cb) {
            callback = cb;
        }
        
        function resolve(value) {
            callback(value)
        }
        
        fn(resolve);
    }

然后重写doSomething()函数:

    function doSomething() {
        return new Promise(function(resolve) {
            var value = 42;
            resolve(value);
        })
    }

从新定义后的doSomething()函数实行后返回取得一个promise实例,实例上有then()要领,能够吸收回调函数。

    doSomething().then(function(value) {
        console.log(value);
    })

然则上面的代码会报错(callback is undefined),是由于:resolve中的callback要早于then()要领中的callback的赋值操纵

那末对Promise组织函数轻微处置惩罚下,把同步的代码运用setTimeout来hack下,转变代码的实行递次,使得resolve函数中的callbackvalue举行处置惩罚前被赋值了。

    function Promise(fn) {
        var callback = null;
        this.then = function(cb) {
            callback = cb;
        }
        
        function resolve(value) {
            setTimeout(function() {
                callback(value);
            }, 1)
        }
        fn(resolve);
    }

这里经由历程setTimeout异步函数转变了代码实行的递次,确保callback被挪用前已被赋值成cb

从新挪用:

    doSomething().then(function(value) {
        console.log(value);
    })
    // 42
    //一般实行。

然则定义Promise组织函数的代码照样有题目的,由于假如仅仅是挪用then()要领而注入回调的话,内部的callback仍然是null。一样不能一般的实行。

别急,慢慢来。

Promises have state

事实上Promise是有状况的:

  • pending

  • resolved

  • rejected

pending => resolved 或许 pending => rejected。状况一旦发作转变,不可逆。接下来,让我们在Promise的组织函数内里到场state,运用state来掌握全部代码流程。

    function Promise(fn) {
        var state = 'pending', 
            value, 
            deferred;
        
        function resolve(newValue) {
            state = 'resolved';
            value = newValue;
            
            if(deferred) {
                handle(deferred);
            }
        }
        
        function handle(onResolved) {
            if(state === 'pending') {
                deferred = onResolved;
                return ;
            }
            
            onResolved(value);
        }
        
        this.then = function(onResolved) {
            handle(onResolved);
        }
        
        fn(resolve);
    }

代码变的比之前越发庞杂。然则如今运用state来掌握代码的流程。then()要领resolve()要领将掌握权交给了新的要领handle(),由handle()要领来依据state的值举行流程操纵:

  • 假如statepending状况,即在resolve()之前挪用了then()要领,那末会将onResolved回调赋值给一个deferred耽误对象deferred对象将这个回调保留起来,稍后当resolve()挪用时,pending状况变成resolved,并挪用deferred对象

  • 假如在then()要领前挪用resolve()要领,pending状况变成resolved,然后挪用then()内里注入的回调onResolved.

经由历程以上的代码,promise能够恣意次数的挪用then()要领:

    var promise = doSomething();
    
    promise.then(function(value) {
        console.log('Got a value:', value);
    });
    // 42
    
    promise.then(function(value) {
        console.log('Got the some value again:', value); 
    });
    //42

然则如许的Promise组织函数照样有题目的,人人能够设想下,在挪用resolve()要领前,挪用了很屡次的then()要领,那末只要末了一个then()要领内里注入的callback才会有效。处理这个题目的要领就是保持一个deferreds行列,去保留每次then()要领注入的回调函数。

Chaining Promises

下面的代码是最一般不过的promise链式挪用:

    getSomeData()
    .then(filterTheData)
    .then(processTheData)
    .then(displayTheData)

getSomeData()要领挪用后会返回一个promise对象,如许便能够挪用then()要领,一样这第一个then()要领挪用后也会返回一个promise对象。如许才继承挪用then()要领。

then()要领老是返回一个promise。

接下来在代码中加以完成:

    function Promise(fn) {
        var state = 'pending',
            value,
            deferred = null;
            
        function resolve(newValue) {
            state = 'resolved';
            value = newValue;
            
            if(deferred) {
                handle(deferred);
            }
        }
        
        function handle(handler) {
            if(state == 'pending') {
                deferred = handler;
                return;
            }
            
            if(!handler.onResolved) {
                handler.resolve(value);
                return;
            }
            
            var ret = handler.onResolved(value);
            handler.resolve(ret);
        }
        
        this.then = function(onResolved) {
            return new Promise(function(resolve) {
                handle({
                    onResolved: onResolved,
                    resolve: resolve
                });
            });
        };
        
        fn(resolve);
    }

在这次的代码中,挪用then()要领后会返回一个新的天生的promise对象。它具有then()要领,能够继承挪用then(),并返回一个新天生的promise对象。云云继承举行下去。这就完成了Promise链式挪用。

再来看看详细的代码完成:
resolve()要领没什么变化,然则handle()要领吸收一个handler对象。handler对象有2个属性,一个为onResolvedthen()要领内里注入的回调函数,用来对传入的上一个promise通报过来的值举行处置惩罚;另一个为resolvePromise组织函数内部定义的resolve()要领,用来转变Promise状况以及value值。

详细分析下handle()函数:

    function handle(handler) {
        if(state === 'pending') {
            deferred = handler;
            return;
        }
        
        if(!handler.onResolved) {
            handler.resolve(value);
            return;
        }
        
        var ret = handler.onResolved(value);
        handler.resolve(ret);
    }

每次挪用then()要领新建一个promise对象历程当中,handle({onResolved: onResolved, resolve: resolve})resolve属性始终是取得的定义历程当中对外部resolve要领的援用。即上一次的promise中定义的resolve.

当then()要领内里注入回调函数时,挪用onResolved要领并取得返回值ret,传入resolve要领,转变state的值以及变动promise中须要继承通报下去的值。假如onResolved要领中会返回处置惩罚过的值,那末下一个promise能拿到这个值,假如onResolved没有返回,传入下一个promise的为undefined**

    doSomething().then(function(result) {
        console.log('First result', result);
        return 88;
    }).then(function(secondResult) {
        console.log('second result', secondResult);
    })
    
    //the output is 
    //
    //First result 42
    //Second result 88
    
    doSomething().then(function(result) {
        console.log('First result', result);
    }).then(function(secondResult) {
        console.log('Second result', secondResult);
    })
    
    //now the output is
    
    //First result 42
    //Second result undefined
    
当then()没有注入回调函数时,仍然会挪用resolve要领,转变state的值,以及猎取上一个promise通报过来的值,并将值通报给下一个promise。
    doSomething().then().then(function(result) {
        console.log('Got a result', result);
    });
    
    //the output is 
    //
    //Got a result 42

主如果得益于handle()要领中,挪用resolve要领猎取从上一个promise取得的value以及作为传入下一个promisevalue:

    if(!handler.onResolved) {
        handler.resolve(value);
        return;
    }
再每次挪用then()要领的历程都邑新建一个pending状况的promise,并经由历程resolve要领转变状况,假如then()要领中注入了回调函数,并返回了值,那末这个值会一向通报下去,假如没有注入回调函数,resolve要领会猎取上一个promise通报过来的值,并作为传入下一个promise的值。即then()要领注入的回调函数是可选的。

经由历程再次对Promise组织函数的增强,完成了promise链式挪用的功用。

关于reject的部分过2天加上。

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