$q promise 是使用 AngularJS 時相當重要的一環,熟悉並活用之後可以讓許多原本看似較雜亂的 event-base 程式碼看起來謹然有序(當然也不是所有的 event-base 程式都適合使用 promise 來實現)。
一段一段的動作是依序進行,但觸發的時候卻又是一次性的(比如說在一個 loop 中一次排定 10 個非同步的工作,需要依序執行)。
var taskIds = [1, 2, 3, 4, 5]; angular.forEach(taskIds, function (taskId) { // 以 $http 為例 $http.get('/someUrl', {params: {taskId: taskId}}); }); console.log('工作順利完成!');
$http.get('/someUrl', {params: {taskId: 1}}) .then(function () { return $http.get('/someUrl', {params: {taskId: 2}}); }) .then(function () { return $http.get('/someUrl', {params: {taskId: 3}}); }) .then(function () { return $http.get('/someUrl', {params: {taskId: 4}}); }) .then(function () { return $http.get('/someUrl', {params: {taskId: 5}}); }) .then(function () { console.log('工作順利完成!'); });
在上面的例子中,工作只有五項所以固然可以自己打一打程式碼解決,一般來說碰到的情況會有下面的困境:
同時可以進行的工作數不只一項(以上面為例即 1, 2 可以同時進行,而 3, 4 可以同時進行,最後再單獨執行 5 即可)
Continue Reading
ngQueue 這個 module 就是為了因應上述的情況而誕生的,特色如下:
與 $q 結合,並提供傳入 context 及 arguments 的功能
更多資訊可以去 http://github.com/pc035860/ngQueue 看看
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.min.js"></script> <script src="http://pc035860.github.io/ngQueue/ngQueue.min.js"></script>
接著在 AngularJS app 中載入 ngQueue module
angular.module('myApp', ['ngQueue']);
首先先用 ngQueue 提供的 $queueFactory 建立 queue instance
// 建立同時執行兩件工作的 queue var queue = $queueFactory(2);
接著可以排入同步的工作(即不是非同步的工作),範例中我們也嘗試傳入自訂的 context 以及 arguments。
queue.enqueue(function (inA, inB, inC) { console.log(this); // {name: "context"} console.log(inA, inB, inC); // hello world ! doSomething(); // 傳入 context 以及 arguments }, {name: 'context'}, ['hello', 'world', '!']);
此類工作與同步工作在排入時最大的區別在於,工作內容最後的 return 值必須是 promise。
使用 $timeout 與 $q - 自訂的 promise 工作
// $timeout delay queue.enqueue(function () { var dfd = $q.defer(); $timeout(function () { dfd.resolve(); // or dfd.reject() }, 100); return dfd.promise; });
使用 $http - AngularJS 內建就會 return promise 的工作
// $http request queue.enqueue(function () { return $http.get('/some/api/call') .success(function () { // do something if success }) .error(function () { // do something if error }); });
這個 demo 演示了 ngQueue 處理排入的同步 / 非同步工作的實際情況。 可以透過上方的按鈕選擇排入「同步」、「非同步」以及「亂數 10 個」的工作,而 queue 的 instance 在建立的時候設定為同時能夠執行兩個工作,因此我們在 demo 中可以看到同時會有兩個數字在倒數。
所以最後回頭修改本篇的第一個範例,得到我們使用 ngQueue 之後的版本。
需要注意的是,如果 taskId 不用 argument 的方式傳入 enqueue 的 function 的話,真的輪到該 function 執行的時候,會無法透過 closure 抓到正確的 taskId。
var queue = $queueFactory(1), taskIds = [1, 2, 3, 4, 5]; angular.forEach(taskIds, function (taskId) { // enqueue with argument: taskId queue.enqueue(function (taskId) { // returns $http promise return $http.get('/someUrl', {params: {taskId: taskId}}); }, null, [taskId]); }); queue.enqueue(function () { // all tasks finished console.log('工作順利完成!') });