AngularJS and Q.fcall

2019-07-21 09:26发布

问题:

Angulars $q is a promise/deferred implementation inspired by Kris Kowal's Q.

In Q, you create a promise with

var myPromise = Q.fcall(myFunction);

Here the myFunction will be invoked asynchronously, a promise is placed in the myPromise variable and the code execution continues.

The only example Angular gives for creating a promise is using javascript timeout function, which to me seems like a verbose hack compared to the above Q example. So in Angular I would write

function asyncWorker(name) {
  var deferred = $q.defer();

  setTimeout(function() {
    scope.$apply(function() {
      deferred.resolve(myFunction);
    });
  }, 1000);

  return deferred.promise;
}

The above would be identical to the one-liner at the top.

I hoped that $q.fcall would have worked but I get:

TypeError: 'undefined' is not a function (evaluating '$q.fcall(function() { return 'a'; })')

So what's the most straightforward way of asynchronously invoking a function and returning a promise in AngularJS?

回答1:

Are you looking for something like this

function doWorkAsync() {
   var defer = $q.defer();
   //do some work async and on a callback 
   asyncWork(function(data)) {
      defer.resolve(data);
   }
   return defer.promise;
}

Now you call this function

doWorkAsync().then(function(data));

Number of angularJS library function already return a promise on invocation. Like $timeout,$http, $resource.



回答2:

I don't know if this is very smart, but it works for me:

function fcall(someValues) {
    var deferrd = $q.defer();
    deferrd.resolve(someValues);
    return deferrd.promise;
}

fcall(123).then(function(values) {
    console.log(values); // 123
});


回答3:

Ok, cleaner alternative is injecting Angular's $timeout and let's say again that function myFunction is the work I want to do asynchroniously, I'll just do:

function doWorkAsync() {
   return $timeout(myFunction, 10);
}

and doWorkAsync will return a promise that will be resolved when myFunction has finished it's work.

For unit testing I can call $timeout.flush() to fire the timeout function instantly.



回答4:

    function createPromise(func) {
        return function (params) {
            var deferred = $q.defer();

            $timeout(function () {
                try {
                    var rtn = func(params);
                    deferred.resolve(rtn);
                }
                catch (ex) {
                    deferred.reject();
                }

            })
            return deferred.promise
        }
    }


回答5:

You can use $q as a constructor similar to how ES6 works.

function asyncGreet(name) {
  return $q(function(resolve, reject) {
    resolve('Hello, ' + name + '!');
  });
}

angular docs



回答6:

You can mimick Q.fcall(fn) using $q.when().then(fn). It is slightly more verbose but it works the same.

var myPromise = $q.when().then(myFunction);

where myFunction can be either asynchronous or synchronous, and will be called with undefined arguments.

Working example:

// Node.js
var Q = require('q');
var updatePromise = Q.fcall(function() {
    if (isUpdate) {
        return doAsyncUpdate();
    } else {
        return doSyncDelete();
    }
}).catch(function(err) {
    handleError(err);
});

// AngularJS
['$q', function($q) {
    var updatePromise = $q.when().then(function() {
        if (isUpdate) {
            return doAsyncUpdate();
        } else {
            return doSyncDelete();
        }
    }).catch(function(err) {
        handleError(err);
    });
}]

Documentation: https://docs.angularjs.org/api/ng/service/$q#when.

Note that $q.when works the same as Q.when.