I have 2 functions:
getDocument: function(title){
var defer = $q.defer();
$timeout(function(){
defer.resolve(true);
console.log(1);
},2000);
return defer.promise;
},
anotherFunc : function(){
var defer = $q.defer();
console.log(2);
defer.resolve(document);
return defer.promise;
}
and a call:
when('/entry/:title', {templateUrl: 'partials/views/entry.php', controller: 'entryCtrl',resolve: {
document: function($q,$route,$log,document){
var defer = $q.defer();
document.getDocument()
.then(document.anotherFunc());
return defer.promise;
}
}}).
Although i have applied a timeout to getDocument(), anotherFunc() get's called, even when the promise has not been resolved yet.
Why is this?
How can i avoid this behaviour?
anotherFunc() get's called, even when the promise has not been resolved yet.
Because you have called it:
… document.anotherFunc() …
^^
Instead, you want to pass a function into then() that will get called when the promise resolves:
….then(document.anotherFunc)
// or, more explicit and preserving 'this':
….then(function(promiseResult) {
document.anotherFunc();
})
Related
I'm trying to setup a test which involves promises. Here is my example code:
var promise;
beforeEach(inject(function ($q) {
promise = $q.resolve();
}));
it('should resolve', function (done) {
promise.then(function () {
expect(true).toBeTruthy();
done();
});
});
For some reason, when I run this, I get a TIMEOUT
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
Why doesn't the promise execute the callback given to then ?
Cheers
You need to call scope/rootScope $digest method to resolve promises.
So it should be:
var result = false;
promise.then(function() { result = true;});
$rootScope.$digest();
expect(result).toBeTruthy();
I understand Angular promises, when Angular makes request with $http, the .then(function(res){ return res }) is supposed to wait until the promise is resolved. Therefore, if I attached the request to a variable, shouldn't the variable's value be that resolved promise?
Service to call the Github API
is.service('githubApiService', function($http) {
this.repo = function(user, repo) {
var value = $http.get('https://api.github.com/repos/' + user + '/' + repo).then(function(json) {
return {
stargazers_count: json.data.stargazers_count,
watchers_count: json.data.watchers_count,
forks_count: json.data.forks_count,
watchers: json.data.watchers,
subscribers_count: json.data.subscribers_count
};
});
return value;
};
})
Directive that calls the Service
is.directive('chart', function(githubApiService) {
return {
restrict: 'E',
template: '<svg margin="[20, 30, 30, 40]", width="750" height="450"></svg>',
link: function(scope, element, attrs) {
var ais = githubApiService.repo('srph', 'angular-infinite-scroll');
var ai = githubApiService.repo('thenikso','angular-inview');
var ns = githubApiService.repo('jankuca', 'ng-scroller');
console.log(ai); // returns a promise!
}
}
})
This is expected behavior. It returns the promise in all scenarios, you need to use .then() to get the data you want:
githubApiService.repo('thenikso','angular-inview').then(function(data){
var ai = data;
});
You can't get the value of the promise like you are expecting.
If you look at $q documentation, you will see that the then method that you are chaining returns also a promise:
This method returns a new promise which is resolved or rejected via the return value of the successCallback, errorCallback (unless that value is a promise, in which case it is resolved with the value which is resolved in that promise using promise chaining)
You therefore have to get the returned value through a success handler:
githubApiService.repo(...).then(function (data) {
// data is the object you are expecting
});
It returns the promise because you do not know when the underlying asynchronous call will be completed.
In some trivial cases this may happen almost immediately, but until you explicitly 'resolve' the fulfilled or rejected promise via the then method, you just have the actual promise.
Consider these two methods where doSomething returns a promise:
myService.doSomething().then(function(data){
return data;
});
myService.doSomething().then(function(data){
//This is some non-trivial calculation that will take a few seconds to do
var myData = transformData(data);
return myData;
});
The promise allows you to deal with both these calls the same way, where you have to resolve it with a then method call and not actually worry if the data is ready or not at that point in your program.
I'm struggling to understand why after calling updateStatus() I see 'promise resolved' logged to the console, but not 'refreshGames'. How is the promise resolved if the code inside refreshGames() never runs?
var refreshGames = function() {
console.log('refreshGames');
var defer = $q.defer();
playersService.getGames({
playerId: playerId
}).$promise.then(function(data) {
vm.games = data;
return defer.promise;
});
};
var updateStatus = function() {
$q.all([refreshGames.promise]).then(function() {
console.log('promise resolved');
populateOptions(vm.games);
vm.tableParams.reload();
});
};
Because your function refreshGames returns nothing, it should return the promise and the defer must be resolved, like this:
var refreshGames = function() {
console.log('refreshGames');
var defer = $q.defer();
playersService.getGames({
playerId: playerId
}).$promise.then(function(data) {
vm.games = data;
defer.resolve(data);
});
return defer.promise;
};
and in the $q.all you just do refreshGames()
refreshGames.promise is undefined - there's no code anywhere that creates this property on refreshGames
any non-promise in $q.all is promisified and effectively equivalent to Promise.resolve(n) (or however you do that with $q
so, your $q.all is essentially
$q.all([undefined]).then(function() {
...
});
and thus gets executed immediately
I am new at AngularJs and very new at Typescript.
I included Typescript in my AngularJs project but couldn't handle a service where i return a $q(function(){...})
my code looks like:
function foo(request, monitor, currentMonitorPropertys) {
var currentChart;
return $q(function (resolve) {
$http(request).success(function (chartResponse) {
...
resolve(monitor);
}).error(function(response){
...
});
});
I work with VS2013(TypeScript), if i implement this method like above, there comes an compilererror: Value of type 'IQService' is not callable. Did you mean to include 'new'?
So how could I implement the function with Typescript.
Thank you for your answer.
There are several ways to return a promise... $http returns a promise with each of its ajax calls, $timeout also returns a promise.
That being said you want to return a promise based upon something other than a scheduled event ($timeout, $interval) via $q you can do this...
// assume $q is injected into your service/controller/factory
// create a defer object
var defer = $q.defer();
// do something...
if (doSomething()){
defer.resolve(); //something went right
else {
defer.reject(); //something went wrong
}
//make sure you return out the promise, so the consumer can act upon it.
return defer.promise;
Also, $q has some nice helper methods to return a promise that you can use when you stub out some logic;
// this will a promise that will resolve with the value provided
return $q.when({some: 'result'});
// this will return a promise that will reject with the error specified
return $q.reject('some error message');
$q isn't a function but a service. If you want a defer, you can use the following code for example:
var deferred = $q.defer();
$http(request).success(function (chartResponse) {
deferred.resolve(monitor);
}).error(function(response){
deferred.reject(response);
});
// return promise
return deferred.promise;
What you can keep in mind if you don't do anything else, you can just return the $http call, because it is a promise itself:
return $http(request);
As you're using $http (that already returns a promise) why not returning this promise directly? Simpler and faster.
function foo(request, monitor, currentMonitorPropertys) {
var currentChart;
return $http(request).then(function (chartResponse) {
//...
return monitor;
});
});
Then when consuming this service you could manage success and error from there, which makes for a tidier implementation:
// for ex. in a controller
foo().then(mySuccessCallback)
.catch(myErrorHandler)
You need to defer $q before resolve it This is code try this one
(function retriveDemoJsonData(){
angular.module('myApp').factory('actionData', function ($q, $http) {
var data={};
data.actionDataJson = function(id){
//The original business logic will apply based on URL Param ID
var defObj = $q.defer();
$http.get('demodata.json')
.then(function(res){
defObj.resolve(res.data[0]);
});
return defObj.promise;
}
return data;
});
})();
----------+
I hope this will help you......
I have a test that requires one promise to be run and later in its then handler another promise returning function is run.
The first promise resolves, and a successful call is made to the next function which returns a promise. However, the then handler for that second promise never fires.
Is there another way to test nested promises using Jasmine 2.0?
Example problem:
describe("nested promise suite", function () {
var prom1 = function () {
var deferred = $q.defer();
$timeout(function () {
deferred.resolve('prom1');
}, 500)
return deferred.promise;
};
var prom2 = function () {
var deferred = $q.defer();
$timeout(function () {
deferred.resolve('prom2');
}, 500);
return deferred.promise;
};
iit("nested promise test", function (done) {
prom1()
.then(function (result) {
console.log('prom1 result ', result);
prom2()
.then(function (result2) {
console.log('prom2 result ', result2);
})
.finally(function() {
console.log('you did it');
})
})
.finally(done); //this causes promise one to resolve properly but unsure of how to make the second promise resolve properly
$timeout.flush();
})
});
I'm not sure if this is the problem in your original code as well, but in this example, your second console.log doesn't fire because prom2 adds a new timeout after the first has been flushed. The promise from prom2 then waits for this new timeout to flush, which never happens:
prom1() is called
The first promise and timeout are created
The first timeout is flushed, resolving the first promise
The then() block is triggered, calling prom2()
The second promise and timeout are created
done() is called.
You can try adding a second $timeout.flush(); call right after the one that's already there, which will flush the second timeout, resolve the second promise, and log the missing messages.