淺談Q的基本實現,淺談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() 傳回值-> promissadd().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) }).then(function(data){ throw new Error() }).catch(function(err){ if(err){ console.log("There is a Error!") console.log(err) } })