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?
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
.
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
});
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.
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
}
}
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
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
.