$q
一個幫助處理異步執行函數的服務。當他們做完處理時,使用它們的返回值(或異常)。
受 Kris Kowa’s Q 的啟發,這是一個實現promise/deferred對象的啟用。
$q的兩種方式---這是一個更類似於Kris Kowal Q或jQuery的遞延實現,另一種在一定程度上類似的ES6承諾。
Deferred Api
一個被$q.defer()調用的deferred的新實例。
deferred對象的目的是暴露相關承諾實例,以及APIs被執行的成功或不成功情況,以及任務的狀態。
方法:
resolve(value):根據value以解決派生的promise。如果值是通過$q.reject構造的rejection 對象,該promise將被拒絕。
reject(reason):根據reason以拒絕派生的promise。這相當通過 $q.reject構造的rejection 對象來解決。
notify(value):在 promise 被執行的過程中提供狀態更新情況。這可能會被多次調用,在promise是被解決還是被拒絕之前。
屬性:
promise:承諾,與這個延遲相關的承諾對象。
Promise Api
當一個deferred實例被創建時,一個promise實例被創建,並且可以通過調用deferred.promise檢索。Promise對象的目的是當它完成后允許需要的地方獲得延遲任務的結果。
方法:
then(successCallback,errorCallback,notifyCallback);
無論什么時候,promise是已經(將要)被解決或拒絕,只要結果是可用的,就調用一個成功/錯誤的回調異步。回調函數帶着一個參數被調用:解決的結果或拒絕的原因。此外,在承諾被解決或被拒絕之前,通知回調可能被調用0或多次用來提供一個指示進度。
這個方法返回被successCallback/errorCallback的解決或拒絕的返回值作為一個新的承諾(除非返回值是個promise,在承諾鏈的承諾和值都被解決的情況下)。它還將通過notifycallback方法的返回值進行通知。promise 不能從notifyCallback方法得到解決或拒絕 。
catch(errorCallback);
promise.then(null, errorCallback) 的快捷方式。
finally(callback,notifyCallback);
允許你觀察一個承諾的解決或拒絕,但這樣做不能修改最后的值。這可用於promise不論是被解決或拒絕后釋放資源或做一些清理。
鏈式承諾
因為調用本次promise的方法將會返回一個新的延生的promise,它很容易創建一個承諾鏈:
promise = promise.then(function(result){ return result+1; });
當一個承諾解決另一個承諾(這將推遲其進一步的解決)可能創建一個任何長度的鏈。它可能在鏈中的任何處暫停/推遲承諾。這使得它可以像$http的響應攔截這類強大的API。
Kris Kowal’s Q和$q的不同
以下是兩個主要的不同:
在Angular里,$q和$rootScope.Scope Scope模型的觀察機制集成,這意味着更快的將解決/拒絕的結果傳播到你的model和避免不必要的瀏覽器重新渲染(這將導致ui的閃爍)。
Q比$q有更多的特性,但這是以字節為代價的。$q是小版本的,但包含所有常見的異步任務所重要的功能。
依賴:$rootScope
使用:$q(resolver);
方法:
defer();
創建一個deferred對象,它代表一個將在將來完成的任務。返回一個deferred的新實例。
reject(reason);
創建一個由指定的理由拒絕的承諾。在承諾鏈中,這個api將被用於承諾的拒絕。如果你正在處理一個承諾鏈的最后一個承諾,那么你不需要擔心它。
reason:常數,消息,異常或一個對象表示拒絕原因。
返回一個已經根據拒絕原因解決了的承諾。
when(value);
將一個對象或者一個值或者一個第三方承諾包裝進$q承諾。當你處理一個可能是承諾或可能不是承諾或承諾來自一個不可信的來源的對象。
value:值或者承諾。
返回一個承諾。
resolve(value);
when的別名,為了與ES6保持一致。
all(promises);
當所有承諾都得到解決后,在一個單一的承諾里集合多個被解決的承諾。
promises:承諾的數組或者哈希。
返回一個將被結合一個數組/哈希或者值解決的單一的承諾,每個值在相同索引/鍵的數組/哈希承諾對應相對承諾,如果有任何承諾被拒絕,這將導致承諾被以相同的拒絕值拒絕。
使用代碼:
(function () { angular.module('Demo', []) .controller('testCtrl', ["$q", testCtrl]); function testCtrl($q) { var fn = function (value) { var defer = $q.defer(); if (value == "Hello") { defer.resolve("Hello World"); } else { defer.reject("hello world"); } return defer.promise; }; var promise = fn("Hello"); promise.then(function (v) { console.log("resolved with " + v); }, function (r) { console.log("rejected with " + r); });//Hello World var anotherPromise = fn(); anotherPromise.then(function (v) { console.log("resolved with " + v); }, function (r) { console.log("rejected with " + r); });//hello world } }());
承諾鏈:
(function () { angular.module('Demo', []) .controller('testCtrl', ["$q", testCtrl]); function testCtrl($q) { var fnA = function (value) { var defer = $q.defer(); if (value == "Hello") { defer.resolve("Hello World -- fnA"); } else { defer.reject("hello world -- fnA"); } return defer.promise; }; var fnB = function (value) { var defer = $q.defer(); if (value == "Hello") { defer.resolve("Hello World -- fnB"); } else { defer.reject("hello world -- fnB"); } return defer.promise; }; var promise = fnA("Hello"); promise.then(function (v) { console.log("resolved with " + v);//Hello World -- fnA return fnB(); }, function (r) { console.log("rejected with " + r); return fnB("Hello"); }).then(function (v) { console.log("resolved with " + v); }, function (r) { console.log("rejected with " + r);//hello world -- fnB }) } }());
.when():
(function () { angular.module('Demo', []) .controller('testCtrl', ["$q", testCtrl]); function testCtrl($q) { var obj = { value: "Hello World" }; $q.when(obj.value).then(function (v) { console.log(v);// Hello World obj = { text: "hello world" } return $q.when(obj.text); }).then(function (v) { console.log(v);// hello world }) } }());
.all():
(function () { angular.module('Demo', []) .controller('testCtrl', ["$q", testCtrl]); function testCtrl($q) { var fnA = function (value) { var defer = $q.defer(); if (value == "Hello") { defer.resolve("Hello World -- fnA"); } else { defer.reject("hello world -- fnA"); } return defer.promise; }; var fnB = function (value) { var defer = $q.defer(); if (value == "Hello") { defer.resolve("Hello World -- fnB"); } else { defer.reject("hello world -- fnB"); } return defer.promise; }; var promiseA = fnA("Hello").then(function (v) { console.log("resolved with " + v); return fnB(); }, function (r) { console.log("rejected with " + r); return fnB("Hello"); }); var promiseB = fnB().then(function (v) { console.log("resolved with " + v); }, function (r) { console.log("rejected with " + r); }); var promises = [promiseA, promiseB]; $q.all(promises); /* result: ** resolved with Hello World -- fnA ** rejected with hello world -- fnB */ } }());
關於$q,本獸目前的理解也就這些,用的話也是看情況着用,更多時候覺得它就像語法糖一般,只是省了N多嵌套,但不得不說這個語法糖讓代碼可讀性和整潔性好了很多。$q更多的好處是在promise和deferred,創建個承諾,在后面需要的地方再回來解決這個承諾。同時也推薦一篇關於描述$q的文章,雪狼大叔寫的,url是:http://www.ngnice.com/posts/126ee9cf6ddb68