淺談Q的基本實現

從整個的角度來說,Q的實現其實是閉包的一個應用,下文簡單說明實現一下Q的基本功能。

GitHub Q的API

實現Q的功能,先了解一下Q的API

通過 https://github.com/kriskowal/q 截取最簡單那部分的文檔如下:

var deferred = Q.defer();
FS.readFile("foo.txt", "utf-8", function (error, text) {
    if (error) {
        deferred.reject(new Error(error));
    } else {
        deferred.resolve(text);
    }
});
return deferred.promise;

可以看到關鍵的api 有 defer, reject, promiss, resolve,然後文檔還涉及到結果的取值,和錯誤捕捉,包括then, catch, done, 就不再貼出來。

入口

首先 從前四個api開始。 通過第一行代碼

var deferred = Q.defer();

那麼可明顯得出的代碼是:

var Q = {
    defer: function(){}
}

然後defer()的返回值deffred擁有3個屬性。

var Q = {
    defer: function(){
        return {
            reject: function(error){},
            resolve: function(data){},
            promiss: null //當前還不能確定是個什麼值
        }
    }
}

以上基本的代碼結構出來了。在來補充promiss的內容。

promiss

問來方便說明,這裏先預設一個使用環境。從結果來推導代碼實現。

          var add = function(){
            var deferred = Q.defer()
            setTimeout(function(){
                deferred.resolve(2)
            }, 2000)
            return deferred.promise
          }

          add().then(function(data){
            console.log(data)
            return data + 1
          }).then(function(data){
            console.log(data)
          }).then(function(data){
            throw new Error()
          }).catch(function(err){
            if(err){
                console.log("There is a Error!")
                console.log(err)
            }
          })

可以看到上面的add就使用了Q. 先看

add().then(...).then(...).catch(...)

那麼可以明確,deffred.promiss至少有三個屬性 (then,catch兩個加上沒有出現的done),並且其中的兩個應該返回的是原對象。所以得到

var Q = {
    defer: function(){

        var promiss = {
            then: function(fn){ //接收回調函數做爲參數
                // ... do something
                return promiss
            },
            catch: function(fn){
                //... do something
                return promss
            },
            done: function(){}
        }

        return {
            reject: function(error){},
            resolve: function(data){},
            promiss: promiss
        }
    }
}

如此以上,整個Q的基本對象結構出來了。雖然沒有實現。

then的實現

  var add = function(){
            var deferred = Q.defer()
            setTimeout(function(){
                deferred.resolve(2)
            }, 2000)
            return deferred.promise
          }

通過上述代碼發現, 計算結果是異步的存儲的,那麼當結果計算完成後,then裏面的函數應該執行。如下

var Q = {
    defer: function(){
        var promiss = {
            then: function(fn){ //接收回調函數做爲參數
                // ... do something
                return promiss //將自身對象返回很容易做到鏈式調用
            },
            ...
        }

        return {
            promiss: promiss
            ...
        }
    }
}

所以

add() 返回值-> promiss
add().then() 返回值-> promiss

接着來看 數據的傳遞。異步後resolve函數得到調用,獲取到了實際結果,這個實際的返回值需要給then裏面的回調函數。這個無法通過直接調用then來實現數據傳遞的。因此需要一箇中間處理函數。如下:

var Q = {
    defer: function(){
       //定義一個結果處理函數。
        var callResult = function(data){
            //do some thing
        }; 

        var promiss = {
            then: function(fn){ //接收回調函數做爲參數
                // ... do something
                return promiss //將自身對象返回很容易做到鏈式調用
            },
            ...
        }

        return {
            resolve: function(data){
               callResult(data)
            }
            ...
        }
    }
}

做到這裏關鍵點來了,需要解決的問題有兩個, 1.怎麼在resolve 函數調用時,把結果傳給then的回調,並且讓它執行? 2.then的返回值怎麼需要給下一個then?

閉包的實現就在這裏了。閉包的主要作用就是隔離作用域,以及向上層上下文尋找變量。

解決關鍵,使用個隊列 來 存儲所then的所有回調,在resolve執行時,執行這個隊列裏面的所有函數,並且把返回值循環賦值。
下面是完整的代碼。

var Q = {
            defer: function(){
                'use strict'
                //正常結果集
                var resultQueue = [];
                //錯誤處理句柄
                var errorHandle = function(error){};

                var promise = {
                    then: function(fn){
                        //存儲每一個then的回調函數,進行統一處理
                        resultQueue.push(fn)
                        return promise
                    },
                    done: function(){
                        return {
                            catch: promise.catch
                        }
                    },
                    catch: function(fn){
                        errorHandle = fn
                    }
                }

                var callAllResultQueue = function(data){
                    for(var i = 0; i < resultQueue.length; i++){
                        data = resultQueue[i](data);
                    }
                }

                return {
                    resolve: function(data){
                        //resolve觸發所有then回調函數的執行。
                        callAllResultQueue(data)
                    },
                    reject: function(error){
                        errorHandle(error)
                    }
                    promise: promise
                }
            }
          }


          var add = function(){
            var deferred = Q.defer()
            setTimeout(function(){
                deferred.resolve(2)
            }, 2000)
            return deferred.promise
          }

          add().then(function(data){
            console.log(data)
            return data + 1
          }).then(function(data){
            console.log(data)
          }).catch(function(err){
            if(err){
                console.log("There is a Error!")
                console.log(err)
            }
          })
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章