Is there an easy way to return a success call to $q.all without having to create a $q.defer() variable?

蓝咒 提交于 2019-12-10 19:18:38

问题


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()
])
.then(function (results) {

Even though I have no need to wait on the syncMethod1() I put this inside the $q.all to keep things simple and to allow me to change the method to async if I wanted in the future.

For the sync functions I am calling them like this:

var syncMethod1 = function () {
    var defer = $q.defer();
    var abc = 99;
    defer.resolve({
        data1: abc,
        data2: 123
    });
    return defer.promise;
};

What I would like to know is if there's a way that I can have the sync method return to the $q.all the data but without my needing to create a defer variable and then do a return of the defer.promise? Just trying to make the sync method as simple as possible.


回答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.




回答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:

 function all(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.

function asyncFirst() {
    var def = $q.defer();

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

    return def.promise;
}

function syncSecond() {
     $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.when source 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.




回答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){
 return function(){
   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) {


来源:https://stackoverflow.com/questions/23780918/is-there-an-easy-way-to-return-a-success-call-to-q-all-without-having-to-create

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!