5

Promise 对象用于延迟(deferred) 计算和异步(asynchronous ) 计算。一个Promise对象代表着一个还未完成,但预期将来会完成的操作。

先来一个例子 A 熟悉下语法

    var p = new Promise(function(resolve, reject){
        console.log('1');
        // 运行后续的 callback 列表,例如:then,when等。否则,不会执行then里面的函数
        resolve('go next'); 
    });
    
    // 只考虑成功
    p.then(function(){
        console.log(2, arguments);
        return 'next';
    }, null).then(function(){
        console.log(3, arguments)
    }, null);
    
    // 输出
    1
    2 ['go next']
    3 ['next']

最初的设想

    function Promise(func){ // 接收一个函数作为参数
    
    }
    
    Promise.prototype = {
    
        then: function(callback){ // 接收函数作为参数
            
            return this; // 支持链式调用
        }
    } 

观察例 A,resolve是一个函数,并且不是用户传的,所有Promise自身应该有一个resolve方法,并且这个方法要传递给Promise构造器里面的函数作为参数。

    function Promise(func){ // 接收一个函数作为参数
        **func(this.resolve);**
    }
    
    Promise.prototype = {
        
        **resolve: function(){
        
        }**,
        
        then: function(callback){ // 接收函数作为参数
            
            return this; // 支持链式调用
        }
    }

Promise是按照顺序来执行callback的,并且由resolve触发。

    function Promise(func){ // 接收一个函数作为参数
        **this.doneList = []; // callback 列表**
        **func(this.resolve.bind(this)); // 顺带绑定this对象**
    }
    
    Promise.prototype = {
        
        resolve: function(){
            **//执行回调列表
            while(true){
                if( this.doneList.length === 0 ){
                    break;
                }
                this.doneList.shift().apply(this);
            }**
        },
        
        then: function(callback){ // 接收函数作为参数
            **this.doneList.push(callback); // 加入到回调队列**
            return this; // 支持链式调用
        }
    }

好,现在写一个测试用例

    var p =new Promise(function(resolve){
            console.log('a');
            resolve();
        }).then(function(){
            console.log('b');
        });
        
     // 输出
     'a'

什么鬼?打印下p

    console.log(p);

图片描述

我们发现原来doneList里面还有一个函数没有运行,再运行下这个函数

    p.doneList[0].call(this);
    
    //结果
    console.log('b'); // 打印 b

打断点跟踪,发现Promise先执行resolve方法,然后执行then,把函数push到doneList。但是,再也没有执行过doneList里面的函数了。怎么办呢?我们可以给Promise加一个属性(state)来描述当前状态,分为【未完成】(pending)和【完成】(done)。如果执行then时,状态已经是完成态,那么切换到未完成态,并执行resolve方法。

    function Promise(func){ // 接收一个函数作为参数
        **this.state = 'pending'; // 初始化状态**
        this.doneList = []; // callback 列表
        func(this.resolve.bind(this)); // 顺带绑定this对象
    }
    
    Promise.prototype = {
        
        resolve: function(){
            //执行回调列表
            while(true){
                if( this.doneList.length === 0 ){
                    **this.state = 'done'; // 回调列表为空,改变状态**
                    break;
                }
                this.doneList.shift().apply(this);
            }
        },
        
        then: function(callback){ // 也是接收函数
            this.doneList.push(callback); // 加入到回调队列
            
            if( this.state === 'done'){
                this.state = 'pneding';
                this.resolve();
            }
            
            return this; // 支持链式调用
        }
    }

用和上面类似的例子再次测试

    var p =new Promise(function(resolve){
            console.log('a');
            resolve();
        }).then(function(){
            console.log('b');
        }).then(function(){
            console.log('c');
        });

结果截图
图片描述

这下,我们自定义的Promise基础功能完成了最核心的部分了。也许有人会疑问,你这写的什么鬼?下面的代码更简单,也能实现相同的功能

    function Promise(func){ // 接收一个函数作为参数
   
        func(this.resolve.bind(this)); // 顺带绑定this对象
    }
    
    Promise.prototype = {
        
        resolve: function(){
            //什么也不干    
        },
        
        then: function(callback){ // 也是接收函数
            callback.call(this); // 直接运行
            return this; // 支持链式调用
        }
    }

测试用例

    var p =new Promise(function(resolve){
            console.log('d');
            resolve();
        }).then(function(){
            console.log('e');
        }).then(function(){
            console.log('f');
        });

结果:
图片描述

但是,文章最前面说过

Promise 对象用于延迟(deferred) 计算和异步(asynchronous ) 计算

并且会顺序执行回调列表(doneList)。最终代码及测试

   function Promise(func){
        this.state = 'pending';
        this.doneList = []; 
        func(this.resolve.bind(this)); 
    }

    Promise.prototype = {
        
        resolve: function(){
            while(true){
                if( this.doneList.length === 0 ){
                    this.state = 'done';
                    break;
                }
                this.doneList.shift().apply(this);
            }
        },
        
        then: function(callback){ 
            this.doneList.push(callback); 
            if( this.state === 'done'){
                this.state = 'pending';
                this.resolve();
            }
            return this; 
        }
    }

    var p = new Promise(function(resolve){
            window.setTimeout(function(){
                console.log('d');
                resolve();
            }, 1000);
        }).then(function(){
            console.log('e');
        }).then(function(){
            console.log('f');
        });

    console.log('被阻塞了吗?');

输出:
图片描述

先输出'被阻塞了吗?',一秒钟后相继输出 d、e、f 。可以看出,不但没有阻塞后面的代码执行,而且回调也是按照顺序执行的。

结束。

下篇继续完善,例如数据传递以及then中函数返回一个Promise时,如何处理。欢迎大家有疑问或者建议,一起来交流。


长颈鹿
1.1k 声望14 粉丝