angularjs系列之輕松使用$q進行異步編程


轉: http://www.myext.cn/javascript/a_6436.html

第一部分關於js中的異步編程

  異步編程簡單的說就是你寫了一段代碼,但他不會按照你書寫代碼的順序立即執行, 而是等到程序中發生了某個事件(如用戶點擊了某個按鈕,某個ajax請求得到了響應)才去執行這段代碼,而且這段代碼可能執行一次(如一個ajax請求得 到了響應)、也可能執行很多次或者不執行(一個按鈕被點擊了許多次或者0次)這就是所謂的異步編程。

  有兩種異步程序模式單次執行模式監聽執行模式。 像ajax請求這樣的就是屬於單次執行模式,請求、回調只會進行一次。像事件綁定就屬於監聽執行模式,只要事件發生回調就可以不斷的執行。但是不管是單次 執行模式還是監聽執行模式在js程序中的共同點都是保存着一個函數引用(這個引用的表現形式就回調函數),在某個事件發生后開始執行這個函數引用中的代 碼。

  下面給出這兩種模式的整體模型圖:

  異步執行體:包含異步執行代碼(或者異步動作)和狀態,狀態不可逆。

  回調執行體:包含回調執行代碼和狀態,狀態不可逆。

  兩種執行體是依靠異步執行體發送信號通信。

  監聽執行模式和單次執行模式的區別:監聽模式中會有多個異步執行體和回調執行體副本,且每個異步執行體副本關聯一個回調執行體副本。多個副本的這兩種執行體的執行代碼是一樣的,唯一區別的就是每個副本的狀態不同。

  代碼舉例:

//單次執行模式:
funtion a(b) {
       console.log(“a process”);
       b();
};
function b() {
       console.log(“b process”);
};
a(b); //發送信號
//監聽執行模式(這里直接使用jquery):
$(“#button”).bind(“click”, function() {
       console.log(“click callback”);
});
$(“#button”).trigger(“click”); //發送信號

第二部分關於promise模式的異步編程

  上面就是異步編程的兩種形式,由於我們的$q是解決第一種模式中存在的問題的,所以這里就討論討論單次執行模式中存 在的問題。你可以設想這么一種場景,當你要在很多個ajax請求響應完成后做一件事情,你用現有的js的回調方式,會不會發現回調的層次很深、代碼十分混 亂。而然我們promise模式的異步編程方式就能很好的管理這些單次執行模式下的代碼,使你的代碼書寫起來更加優雅,說白了promise不產生新的東西只是一個語法糖使你編寫出更加優雅的異步程序代碼。

  那promise模式到底是什么樣的呢,說白了就是把把我上面的單次執行模式圖抽象化了,用一個defer對象(延 期對象)代表異步執行體,用一個promise對象(承諾對象)代表回調執行體,這個defer對象可以發送消息,promise接受消息,然后執行相應 的回調函數。Promise就是異步執行體和回調執行體之間的橋梁,這樣的好處是異步執行體和回調執行體這種模型很好的對異步動作和回調動作進行了解耦。 你可以在promise上面好好的操縱你的回調執行體,而只是接受一個來自defer發送的參數。

  這里還不能很好的體現出promise模式的優勢,而她真正的優勢是在這種模型下擴充的api使其發揮了真正的強大 作用。比如說promise對象的then方法,這個方法就是支持異步鏈式編程最重要的方法,他也是使代碼更加優雅最重要的方法。還有比如說all接收一 個promise數組返回一個新的promise,當前面的所有promise狀態都完成之后這個新的promise才能完成,這個很適合多個ajax后 處理某些事情。不過可能你還不能明白,下面在介紹$qAPI的時候有詳細的介紹。

第三部分 $q服務的API詳解

  下面我們通過講解$q的API讓你更多的了解promise異步編程模式。$q是做為angularjs的一個服務而存在的,只是對promise異步編程模式的一個簡化實現版,源碼中剔除注釋實現代碼也就二百多行,下面開始介紹$q的API。

     defer對象(延遲對象)可以通$q.defer()獲取,下面是defer對象的api:

     方法:

     resolve(value):向promise對象異步執行體發送消息告訴他我已經成功完成任務,value即為發送的消息。

     reject(value): 向promise對象異步執行體發送消息告訴他我已經不可能完成這個任務了,value即為發送的消息。

     notify(value): 向promise對象異步執行體發送消息告訴他我現在任務完成的情況,value即為發送的消息。

  這些消息發送完promise會調用現有的回調函數。

     屬性:

     promise即與這個defer對象的承諾對象。

  從上可以看出defer主要是用來發送消息的。

  promise對象可以通過defer.promise獲取,下面是promise對象的api:

  方法:

  then(successCallback,errorCallback,notifyCallback):參 數為不同消息下的不同回調函數,defer發送不同的消息執行不同的回調函數,消息作為這些回調函數的參數傳遞。返回值為回一個promise對象為支持 鏈式調用而存在。當第一個defer對象發送消息后,后面的promise對應的defer對象也會發送消息,但是發送的消息不一樣,不管第一個 defer對象發送的是reject還是resolve,第二個及其以后的都是發送的resolve,消息是可傳遞的。

  catch(errorCallback):then(null,errorCallback)的縮寫。

  finally(callback):相當於then(callback,callback)的縮寫,這個finally中的方法不接受參數,卻可以將defer發送的消息和消息類型成功傳遞到下一個then中。

  代碼舉例:

function f1(num) {
       document.write("success:" + (num++) + "<br/>");
       return num;
}
function f2(num) {
       document.write("errror:" + (num++) + "<br/>");
       return num;
}
var defer = $q.defer();
var promise = defer.promise;
//方式1
// promise.then(f1,f2).then(f1,f2);
// 方式2
// promise.then(f1,f2);
// promise.then(f1,f2);
// 方式3
// promise.then(f1,f2).then(f1,f2);
// promise.catch(f2);
// promise.finally(f2);
//方式4
// promise.finally(f2).then(f1,f2);
defer.reject(1);
方式1的結果:
errror: 1
success: 2
方式2的結果:
errror: 1
errror: 1
方式3的結果:
errror: 1
errror: 1
error: NaN
success: 2
方式4的結果:
Error: NaN
Error: 1  

  現在繼續$q的api:

  方法:

  defer():用來生成一個延遲對象 var defer =$q.defer();

  reject():參數接收錯誤消息,相當於在回調函數中拋出一個異常,然后在下一個then中調用錯誤的回調函數。

  代碼舉例:

var defer = $q.defer();
var promise = defer.promise;
promise.then(function() {
       return $q.reject("success error");
}, function() {
       return $q.reject("error error");
}).then(function(info) {
       document.write("s:" + info + "<br/>");
}, function(info) {
       document.write("e:" + info + "<br/>");
});
//方式1
// defer.reject(1);
//方式2
// defer.resolve(1);
方式1運行結果
e: error error
方式2運行結果
e: success error

  

  all()參數接收為一個promise數組,返回一個新的單一promise對象,當這些promise對象對應defer對象全部解決這個單一promise對象才會解決,當這些promise對象中有一個被reject了,這個單一promise同樣的被reject了。

  代碼舉例:

var defer1 = $q.defer();
var promise1 = defer1.promise;
promise1.then(function(num) {
              console.log("success" + num);
       },
       function(num) {
              console.log("error" + num);
       });
var defer2 = $q.defer();
var promise2 = defer2.promise;
promise1.then(function(num) {
              console.log("success" + num);
       },
       function(num) {
              console.log("error" + num);
       });
var promise3 = $q.all([promise1, promise1]);
promise3.then(function(num) {
       console.log("s:" + num);
}, function(num) {
       console.log("e:" + num);
});
//方式1
// defer1.resolve(1);
// defer2.resolve(1);
//方式2 
//defer1.reject(1);
方式1的結果:
success1
success2: 1
s: 1,
1
方式2的結果:
error1
e: 1

  

  when():接收第一個參數為一個任意值或者是一個promise對象,其他 3個同promise的then方法,返回值為一個promise對象。第一個參數若不是promise對象則直接運行success回調且消息為這個對 象,若為promise那么返回的promise其實就是對這個promise類型的參數的一個包裝而已,被傳入的這個promise對應的defer發 送的消息,會被我們when函數返回的promise對象所接收到。

  代碼舉例:

var promise = $q.when(1, function(num) {
       console.log("s" + num);
}, function() {
       console.log("e");
});
var defer1 = $q.defer();
var promise1 = defer1.promise;
var promise2 = $q.when(promise1, function(num) {
       console.log("s" + num);
}, function() {
       console.log("e");
});
defer1.reject(1);
運行結果:
s1
e

  對上面還有一個注意事項就是defer對象發送消息不會立即執行的,而是把要執行的代碼放到了rootScope的evalAsync隊列當中,當時scope.$apply的時候才會被promise接收到這個消息。

  到這里$q服務全部介紹完了,對於angular中的then的鏈式調用中如果defer發送的reject的那么 只有第一個promise是reject的回調,其他的都是resolve的回調這里多少覺得是不合適的,不知道是個bug還是就是這樣設計?$q只適合 單次執行模式,不知道是否適合擴展成監聽執行模式?這都是大家值得思考的問題。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM