從整個的角度來說,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)
}
})