AngularJs chaining promisses and using notify - angularjs

When I have multiple promises and resolve them one by one I can easily get notifications from each promise while it is being resolved.
However it seems that when I chain promises or use the all() method then some notifications are getting lost. I am wondering why this is the case.
Here is an example. Lets say I have two async functions that return a promise, do some work and while doing this they call notify one or more times:
function returnsSomePromise1() {...; setTimeout(...); return promise;}
function returnsSomePromise2() {...; setTimeout(...); return promise;}
Now I have three options to resolve the promises:
var promise1 = returnsSomePromise1();
var promise2 = returnsSomePromise2();
//Option 1: Resolve separate
promise1.then(...);
promise2.then(...);
//Option 2: Resolve chained
promise1.then(promise2).then(...);
//Option 3: Resolve with all()
$q.all([promise1, promise2]).then(...);
In each then function I attach a simple function to log the notifications to the console:
.then(..., ..., function(update) { console.log(update); });
Now I find it interesting that all three options yield different results when it comes to notify. The first one prints all notifications of each async operation. The second option only prints the notifications of the first promise, the last option using the all() method does not print any notifications at all.
Can anyone explain what is causing these differences and if it is possible to get notifications when using $q.all()?

Option 1 will fire the promises in parallel, they will be resolved individually. If you want to do something after both have resolved you would need to add extra logic using this option
Option 2 will fire the promises one after the other. This is useful if promise2 depends on something from promise1.
Also, You code should be promise1.then(returnsSomePromise2).then(...); it wants a function in .then() where before it was getting a promise (promise2). The function it calls will return the promise and it will chain it.
Option 3 will fire them in parallel and resolve a function when both (all) have been completed. $q.all() can take an array OR an object. In the case o an array, which you have in your example. The function passed to .then() will be passed an array. The array lines of with the order of the promises passed to $.all().
$q.all([promise1, promise2]).then(function(result){
var promise1Result = result[0];
var promise2Result = result[1];
})
If you pass an object, with named keys, you will get an object back in the function passes to .then(). The keys will line up with the object passed to $q.all()
$q.all({myFirstPromise:promise1, secondPromise:promise2}).then(function(result){
var promise1Result = result.myFirstPromise;
var promise2Result = result.secondPromise;
})

Related

Remove $promise and $resolved from json object not working

I'm getting data from the server using $resource like this
service
.factory('rulesService', ['$resource', function ($resource) {
var systems = $resource('url');
return systems;
}]);
controller
$scope.rules= rulesService.query();
console.log($scope.rules);
The output I get is
0: Resource
1: Resource
$promise: Promise
$resolved: true
length: 2
I tried to strip $promise & $resolved using
1) angular.toJson($scope.rules)
2)JSON.stringify($scope.rules, null, 2)
Both these are returning []
Can someone help me on this
After reading your comments above, I think your problem is you use $resource wrong.
$resource will return a empty object or array, and then make the http call in the background and populate the array/object once it is complete.
This means that, at the time you console.log the object, it is actually an empty array, but since the log in the browser is pretty smart, it will update the object in the log as well, once the $resource call is done.
this is why console.log(rules[0]) is undefined, while console.log(rules) says the element exists. It didn't at the time of the log.
If you need to do further processing you have to do something like:
var rules = rulesService.query(function () {
console.log(rules[0])
})
you can also use promises instead of a callback, but either way you need to ensure the data is fully loaded
You should be able to simply iterate over the array (and ignore the extra properties). If you want a clean array you could always use a map or similar.
$scope.new_rules = $scope.rules.map(function (rule){
return rule;
})
Your problem is related to asynchronous execution and race conditions.
You're trying to refer to rules[0] before data actually arrives from the $resource.query() call.
Examples:
var rules = rulesService.query();
console.log(rules[0]); //will print nothing. the GET request hasn't been resolved yet.
rules.$promise.then(function (response) {
console.log(rules[0]); //This WILL work assuming you actually get data from the backend.
console.log(response[0]); //This will also work with the same data.
});

Asynchronous call defer and promise chain

I stuck with ashync call chain. I tried google but couldn't found exact answer.
requirement is I have a method
callApi(){
I am calling 4 service that all are asynchronous.
I want to chain all the asynchronous calls so that I can do
defer.resolve only when all request are done
}
Any help would be great help.
Thanks in Advance.
You can just use $q.all(). It takes an array of promises and returns a promise, that will resolve when all promises in that array have resolved.
Example:
function callMultipleServices() {
return $q.all([
//Just some random functions returning promises...
someAsyncService(),
$http.get('http://google.de'),
someOtherAsyncService()
]) //.then(function(resultArray) { return doSomethingWith(resultArray) })
}
The returned promise will resolve with an array, containing the resolved values of the promises you passed in. If you want your promise to return a single value that is somehow derived from the service results, just add a .then that takes the results and somehow calculates your final promise result (as in the comment above)

the argument of the promise object's method then()'s callback function

I am new to angular, I did some research but still cannot find an answer...,
I have the following code:
promiseB = promiseA.then(function(result) {
return result + 1;
});
Why we can use result as the argument of the callback function? How the returned stuff of promiseA becomes result ?
Thanks everyone!
A promise is simply an object used to represent the pending activity, don't confuse it with the result of that activity.
The resolved value is passed to the success callback that was given to the .then() method. If promises are chained together then the value returned from that callback becomes the value passed to the next .then() (or if the value returned is also a promise the resolution of that promise is passed to the next .then()).
So in your particular example we don't know where result came from: whatever created promiseA simply ensured it resolved with that value. However we do know that promiseB will resolve with the value result+1 because that's what you returned.
When promiseA completes ("resolves"), it will call the function that you pass to the then() function, and pass the result into that function. So let's say I had a promise that is requesting information from a database query. Let's say it's going to (eventually) give me the phone number for a user. I'd set up my code to print that out like this:
myPromise.then(function(phoneNum) {
console.log(phoneNum);
});
The reason we can't just say var result = myPromise() is because a promise executes in a different flow that the rest of the code. We need to set up a function to handle the result when it's done
Check how using the $q service you can defer the promise returned and process in sucesives callbacks, the defer.promiseit's the key.
var promiseB = myService.response().then(function(result){
var defer = $q.defer();
defer.resolve(result);
return defer.promise;
});
promiseB.then(function(result){
console.log("In promise B ===>", result);
});
check this codepen: http://codepen.io/gpincheiraa/pen/ONjMrR?editors=0010

Angular and Meteor flicker on page load

I had an issue with and angular-meteor project flickering every time a state using the campaigns subscription, would load. By flickering, I mean the data was there, then it would go away and come back a half second later.
I added this to the resolve property of the state (using ui-router):
campaigns: ($q) => {
var deferred = $q.defer();
Meteor.subscribe('campaigns', {
onReady: deferred.resolve,
onStop: deferred.reject
});
return deferred.promise;
}
The flickering stopped, but I don't really understand this code. Can someone who understand angular break this resolve/defer situation down?
Just not sure why it worked. thanks.
$q is angular's implementation of promises.
in a very itty bitty nutshell, a promise has two callbacks that resolve when data is returned; a resolve function if the call succeeds and a reject if the call fails. whatever data it gets will be passed into these functions (essentially doing deferred.resolve(data) or deferred.reject(error)) . $q.defer() allows us to assign the resolution/rejections later.
meteor's subscribe function takes a few arguments. the string name of the collection, a function that returns an array of arguments to be passed to the collection, and an object/function. the object part of the final argument expects an "onReady" and "onStop" functions, and will execute those functions and pass along any data it gets. we pass in our callbacks here.
finally, we return the promise. resolve.campaigns will be a promise, which we can get the values from using the .then(successCallback, failureCallback) call. meteor handles this behind the scenes.

Resolving multiple promises in routeprovider

So I wanted to execute 2 http calls to get some groups and questions from my server and have both of these resolved in routeprovider so that the corresponding controller will not load before I have the relevant data.
In my other controllers I always worked with an initalData object to hold the data.
The resolve part:
resolve: {
initialData: ["GroupsService" "QuestionsService", function (GroupsService, QuestionsService) {
return {
questions: QuestionsService.getAll(),
groups: GroupsService.getAll()
}]
}
When I tried to access the data in the controller using initialData.questions and initialData.groups respectively, I however received 2 promises instead of the data, even though the callback from the http got logged before the controller was instantiated.
QuestionsCtrl.$inect = ["DialogsService", "initialData", "QuestionsService"];
function QuestionsCtrl(DialogsService, questions, groups, QuestionsService) {
//Initialdata object which has 2 Promise properties
//This logs AFTER the callback in both http callbacks
console.log('controller initialized', initialData);
When I replaced the code with this (didn't use an initialData object, instead returned two other objects, it did work:
resolve: {
questions: function (QuestionsService) {
//$http call for all questions
return QuestionsService.getAll();
},
groups: function (GroupsService) {
//$http call for all groups
return GroupsService.getAll();
}
}
Does anyone have any logical explanation for why, in the first case I got back promises (despite that the data was actually present in the client), and the second one worked flawlessly?
When you pass resolve to a route it calls $q.all on it implicitly for you. So when you return multiple values in resolve it waits for all of them to finish.
In your example - you just returned an object containing some values that are promises - you didn't wait for them so it got resolved immediately with the promises instead of unwrapping them.
You can of course explicitly wait for them too:
initialData: ["a" "b","$q", function (a, b, $q) {
return $q.all({
questions: a.getAll(),
groups: b.getAll()
});
}]
If you wish resolve to wait, you need to return a promise, in your first case it's not a promise, rather it's just an object, it happens to have 2 objects that are however promises, but angular won't know that... If you wish to return it as a single resolve you can use:
return $q.all({ key1: promise, key2: promise });
and add $q as a dependency
Another thing, promises doesn't turn them self into raw values when data is received from the server, they stay promises, in the case of resolve, angular will dig out the resolved value and provide those instead of the promises. And again we need to go back to that angular needs to know it is dealing with promises.

Resources