简体   繁体   中英

How can I delay execution of a function until two others have completed using $q?

I am using the AngularJS implementation of $q.

Given the following functions:

   doTask1: function ($scope) {
       var defer = $q.defer();
       $http.get('/abc')
           .success(function (data) {
               defer.resolve();
           })
           .error(function () {
               defer.reject();
           });
       return defer.promise;
   },

   doTask2: function ($scope) {
       var defer = $q.defer();
       var x = 99;
       return defer.promise;
   },

I know I can delay execution of another function like this:

os.doTask1()
    .then(function () {
        doTask3();
});

I would like to start off doTask1 and doTask2 at the same time. Is there a way I can do this and still delay execution so that doTask3() will not execute until doTask1 and doTask2 have ended with success.

$q.all is what you're looking for. ( Documentation )

$q.all takes multiple promises, either in an array or object, and returns a brand new promise that will be resolve when all the passed-in promises are resolved. If you passed in an array of promises, the new promise is resolved with an array of values which correspond to the promises:

$q.all([doTask1(), doTask2()]).then(function(results) {
  // results[0] == result of doTask1
  // results[1] == result of doTask2
  doTask3();
});

If you passed in an object of key-promise pairs, it will be resolved with an object with keys that match the object you passed in, each value nicely corresponding to the value of that key's resolved promise:

$q.all({one: doTask1(), two: doTask2()}).then(function(results) {
  // results.one == result of doTask1
  // results.two == result of doTask1
  doTask3();
});

Because of promise chaining (eg, when you return a promise from a then function, it creates a new promise that resolves to the resolved value of the promise you returned), you can do some cool stuff:

var bothPromises = $q.all([doTask1(), doTask2()]);

var task3Promise = bothPromises.then(function(results) {
  var result1 = results[0];
  var result2 = results[1];
  return doTask3(result1, result2);
});

task3Promise.then(function(resultOfDoTask3) { ... });

It is worth nothing that if any of the promises passed to $q.all is rejected, the promise that is returned will also be rejected. See the Angular $q documentation for more information.


This is (barely) tangential to the question, but I think it's a neat trick: if you happen to use CoffeeScript, you can use destructuring to get to the promises' resolved values.

For arrays:

$q.all([doTask1(), doTask2()]).then ([result1, result2]) ->
  # ...

And for objects

$q.all(one: doTask1(), two: doTask2()).then ({one, two}) ->
  # note that you have to use the same key names
  # as in the object you passed to `$q.all`

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM