Skip to content Skip to sidebar Skip to footer

Is There An Easy Way To Return A Success Call To $q.all Without Having To Create A $q.defer() Variable?

My application has a number of synchronous and asynchronous (doing a $http call) methods. In the controllers I have code like this: $q.all([ asyncMethod1(), syncMethod1()

Solution 1:

$q.all can, even if not documented, take plain values as well and not only promises (it will automatically convert them). Your sync method should just do

return {
    data1: 99,
    data2: 123
};

which is the most simplest thing (and can be used in really synchronous contexts as well).


How can I make a promise from a value without using tedious deferreds?

You can use $q.when:

return $q.when({
    data1: 99,
    data2: 123
});

If the value is not already a promise, then a promise will be returned that is resolved with the value as soon as possible. Notice that this will necessarily introduce asynchrony in your code, so it's not really a syncMethod any more.

Solution 2:

TL;DR : Yes, just return normal values from sync method and use them in $q.all as input params. It will handle them correctly.

Long answer

If we look into the angular code for $q.all we see at this line how input params are handled:

functionall(promises) {
     ....
     forEach(promises, function(promise, key) {
         ....
          ref(promise).then(function(value) {

So each param is passed to the ref function defined at this line. ref takes an argument and if it is a promise it returns it

if (value && isFunction(value.then)) return value;

if it isn't then the value is wrapped with a new created promise which gets returned. That promise gets resolved as soon as possible but not in this event loop iteration.

return {
  then: function(callback) {
    var result = defer();
    nextTick(function() {
      result.resolve(callback(value));
    });
    return result.promise;
  }
};

This means you can safely return non promise values form your sync methods.

functionasyncFirst() {
    var def = $q.defer();

    $timeout(function(){
      $scope.first = true;
      def.resolve();
    }, 1000);

    return def.promise;
}

functionsyncSecond() {
     $scope.second = true;
    return {
        data1: 'abc',
        data2: 123
    };
}

$q.all([
    asyncFirst(),
    syncSecond()
])
.then(function(){
    $scope.all = true;
});

See it in action in this jsbin example

EDIT: As user @Bergi suggests any regular value could be converted to the promise if needed with $q.whensource However, $q.when uses the ref function to convert value to a promise and resolves the promise at the next event loop iteration. Strictly speaking the method itself is sync as it returns without the delays. But the result is immediately wrapped into the promise and not going to be used until next event loop iteration. This means that the sync method isn't used as such. At least not in a away most people imagine sync methods. The overall result of $q.all is going to use such sync methods as async but resolved in the next iteration. Consider this caveat.

Solution 3:

If you need to do a bit more logic than just returning values, and you're doing it for a lot of sync functions, you might want to create a wrapper function that does the deferred logic for you:

var makeAsync = function(fn){
 returnfunction(){
   var deferred = $q.defer();       
   deferred.resolve(fn.apply(this,arguments));
   return deferred.promise;
  };
}

Then you can do this:

$q.all([
    asyncMethod1(),
    makeAsync(syncMethod1)()
])
.then(function (results) {

Post a Comment for "Is There An Easy Way To Return A Success Call To $q.all Without Having To Create A $q.defer() Variable?"