1. 程式人生 > >使用AngularJS的$http服務與服務端進行資料互動

使用AngularJS的$http服務與服務端進行資料互動

$http服務是基於$q服務的,提供了promise封裝,它接受一個配置物件引數,並返回一個promise物件。同時,它還提供了2個方法用來定義Promise回撥:success 和 error。

    var promise = $http({method:"GET", url:"/someUrl"}).  
        success(function(data, status, headers, config){  
            //當非同步請求成功返回響應時觸發  
        }).error(function(data, status, headers, config){  
            //當發生異常時觸發  
        });  

        promise.then(function(result) {
            //result.data即服務端返回的資料物件
        });


$http會返回一個promise物件,瞭解一下angularjs的非同步程式設計,能夠讓你更深入理解以上程式碼

angularjs的非同步程式設計

 第一部分關於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只適合單次執行模式,不知道是否適合擴充套件成監聽執行模式?這都是大家值得思考的問題。