實現簡易 ES6 Promise 功能 (一)

Promise 對象用於延遲(deferred) 計算和異步(asynchronous ) 計算。一個Promise對象表明着一個還未完成,但預期未來會完成的操做。app

先來一個例子 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']

最初的設想async

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; // 支持鏈式調用
        }
    }

好,如今寫一個測試用例this

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

什麼鬼?打印下pspa

console.log(p);

圖片描述

咱們發現原來doneList裏面還有一個函數沒有運行,再運行下這個函數prototype

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

打斷點跟蹤,發現Promise先執行resolve方法,而後執行then,把函數push到doneList。可是,再也沒有執行過doneList裏面的函數了。怎麼辦呢?咱們能夠給Promise加一個屬性(state)來描述當前狀態,分爲【未完成】(pending)和【完成】(done)。若是執行then時,狀態已是完成態,那麼切換到未完成態,並執行resolve方法。code

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時,如何處理。歡迎你們有疑問或者建議,一塊兒來交流。

相關文章
相關標籤/搜索