Reputation: 1708
I've created a simple example of this with the below code. The goal is to be able to used the deferred
promise from Angular's $q
service but have $q
within a callback that itself returns a result that is to be handled in the main controller.
I recognize the error is clearly that the $q
's promise needs to be returned immediately so that it can "await" the result and that placing that promise within a callback inhibits it from being returned immediately. Thus, the code below is clearly an incorrect strategy.
My question is to ask what the best practice would be to achieve a comparable utility to the above described desire, including the presence of both a callback and need to return a promise.
function asyncGreet(name, cb) {
cb(name)
}
function okToGreet(name) {
return name.length > 10
}
var promise = asyncGreet('Robin Hood', function(name) {
var deferred = $q.defer();
setTimeout(function() {
deferred.notify('About to greet ' + name + '.');
if (okToGreet(name)) {
deferred.resolve('Hello, ' + name + '!');
} else {
deferred.reject('Greeting ' + name + ' is not allowed.');
}
}, 1000);
return deferred.promise;
});
promise.then(function(greeting) {
console.log('Success: ' + greeting);
}, function(reason) {
console.log('Failed: ' + reason);
});
Upvotes: 2
Views: 236
Reputation: 1708
Well I actually figured it out. You create deferred
and then pass it into the callback. Should have been obvious to me before I posted this but perhaps it'll help someone else who got confused as I did:
function asyncGreet(name, cb) {
var deferred = $q.defer();
setTimeout(function() {
var foo = null;
cb(name, deferred)
}, 1000);
return deferred.promise;
}
var promise = asyncGreet('Robin Hood', function(name, deferred) {
if (name.length > 10) {
foo = 'Hello, ' + name + '!';
} else {
foo = 'Greeting ' + name + ' is not allowed.';
}
deferred.resolve(foo);
});
promise.then(function(greeting) {
console.log('Success: ' + greeting);
}, function(reason) {
console.log('Failed: ' + reason);
});
Upvotes: 2
Reputation: 49590
To consolidate the comments and to address the other answer's somewhat confusing description of an async API, I decided to provide an answer:
If we suppose that there is some async non-promise / callback-based API, for example, asyncGreet
, it could be mocked like so:
function asyncGreet(name, cb){
// simulate async
setTimeout(function(){
someCondition ? cb({message: "Hello, " + name + "!"}) :
cb({error: "Greeting " + name + " is not allowed."});
}, 2000);
}
(for the purposes of this example, asyncGreet
is a 3rd party API not in our control)
To convert this to a $q
promise-based API, you would use a $q
(either with $q.defer
or with $q
constructor - in fact, I just noticed that Angular's documentation shows the $q
-constructor approach).
So, for example, you could create a greeterSvc
service:
app.factory("greeterSvc", function greeterSvcFactory($q){
return {
greet: function(name){
return $q(function(resolve, reject){
asyncGreet(name, function cb(data){
if ('error' in data) {
reject(data.error); // extract reason
} else {
resolve(data.message); // extract greeting message
}
});
});
}
}
})
The consumer of the greeterSvc.greet
API could then .then
it and, for example, log something - just like you did (although I would use .catch
instead)
greeterSvc.greet("Robin Hood")
.then(function(greeting) {
console.log('Success: ' + greeting);
})
.catch(function(reason) {
console.log('Failed: ' + reason);
});
Upvotes: 2