struggling to get promises working correctly in angularjs service provider
i've read the docs as well as numerous examples (here, here and here) and i think i've got my syntax ok
(although obviously something is wrong)
app module and controller look like
var myApp = angular.module('myApp', []);
myApp.controller('Controller_1', ['$scope', 'Service_1', function($scope, Service_1) {
var myName = "Ben";
Service_1.slowService(myName)
.then(Service_1.fastService(name));
$scope.myName = myName;
}]);
the service (with the slow function) looks like this:
myApp.service('Service_1', function($q) {
this.slowService = function (name) {
var deferred = $q.defer();
console.log('Start of slowService:', name, Date.now());
setTimeout(function() {
console.log('setTimeout name:', name, Date.now());
if(name){
name = 'Hello, ' + name + " is learning Angularjs";
alert(name);
console.log('name:', name);
deferred.resolve(name);
} else {
deferred.reject('No name supplied !');
}
}, 3000);
return deferred.promise;
};
this.fastService = function(name){
console.log('Start of fastFunction:', name, Date.now());
alert('Hello ' + name + ' - you are quick!');
};
});
console output looks like this:
Start of slowService: Ben 1420832940118
Start of fastFunction: result 1420832940122
setTimeout name: Ben 1420832948422
name: Hello, Ben is learning Angularjs
The fastService
is starting before the slowService
completes, despite using a deferred object / promise in Service_1
and a .then
in the controller...
Can anyone point out what's wrong with the code ?
jsfiddle is here
EDIT: put the fast function in the service so there's no confusion with hoisting etc - still the same result - js fiddle updated
The fastService is starting before the slowService completes
That is because you are executing the function fastService before slowService async callback runs.Instead you would want to provide the reference of the function. i.e .then(Service_1.fastService(name));
should be .then(Service_1.fastService);
or .then(function(name){ Service_1.fastService(name); });
otherwise fastservice will just run right away, before the async part of the slowService runs.
Use $timeout
with that the advantage is that it already returns a promise so you do not need to create a deferred object and resulting in deferred anti-pattern.
myApp.service('Service_1', function($q, $timeout) { //<-- Inject timeout
this.slowService = function (name) {
console.log('Start of slowService:', name, Date.now());
return $timeout(function() {
console.log('setTimeout name:', name, Date.now());
if(name){
name = 'Hello, ' + name + " is learning Angularjs";
return name; //return data
}
//Reject the promise
return $q.reject('No name supplied !');
}, 3000);
};
//...
});
and when consuming just chain through:
Service_1.slowService(myName)
.then(Service_1.fastService);
So instead of timeout even if you are using $http
in your original method just return the promise from http rather than creating a deferred object. Also just keep in mind when you use the syntax .then(Service_1.fastService);
and if you are referring to this
context in the fast service it wont be the service instance.
The way you are passing your function to then()
part is not correct.
Pass it like this :
Service_1.slowService(myName)
.then(function(){fastFunction(name)});
Since your slowService
resolves with a name
value, that is what is going to be provided as the parameter of the function you pass into then
. You could do this:
Service_1.slowService(myName)
.then(function(name){ Service_1.fastFunction(name); });
but even this is redundant since you are just handing off the name
to another function that takes a single name
parameter.
This is all you need:
Service_1.slowService(myName)
.then(Service_1.fastFunction);
Notice that there is no (name)
in that second line. We want to pass the function into then
. What you were doing was calling the function immediately and passing undefined
into then
.
YAAK is correct. You could also write it like this:
Service_1.slowService(myName)
.then(fastService);
Just to clarify a bit on what I'm pretty sure is going on:
The then
function is registering callbacks for when the promise is either resolved or rejected. You don't need to pass the function parameters, because that is already done when you resolve
with whatever data you are trying to pass to the callback function. When you had fastService(name)
in there, it was just executing the function as soon as it hit that line of code without waiting for the promise to resolve since it was a function call, and not a function object.
It's interesting that fastService returned 'result' as name instead of saying that name is undefined. I don't know where it is getting a value for that variable from.