angular js code in .then function executed before promise finishes - angularjs

hmmm, i am stuck on this for a while, hopefully i can get some hints from you guys.
i put some sample code here to illustrate the issue:
http://jsfiddle.net/HB7LU/18216/
so basically, i am expecting console.log('out put data for member ' + number); get executed right after mySvc.get() rest call finishes every time i click on the member item.
$q.all([
mySvc.get({id: number})
]).then(function() {
console.log('out put data for member ' + number);
});
but it is not the case, it only works as expected the first time you click on it. second time you click on it, the opposite happens.
XHR finished loading: GET "http://fiddle.jshell.net/HB7LU/18216/show/test?id=1"
(index):53 loading data - rest call finished
(index):68 out put data for member 1 <- this is correct
(index):68 out put data for member 2 <- this is wrong, should wait till rest call finishes
XHR finished loading: GET "http://fiddle.jshell.net/HB7LU/18216/show/test?id=2
(index):53 loading data - rest call finished
ps: i tested it in chrome. havent tested it in other browsers
please let me know how i can fix it. thanks!

You are returning the same deferred each time instead of creating a new one, if you moved the creation of the deferred inside the get call or just returned the promise from the $http call it works fine
myService.get = function(requestParams) {
var deffered = $q.defer();
http://jsfiddle.net/jc04arnn/

You need to create a new deferred each time they call .get(). See the corrected code. Because you can only resolve a deferred once, your resolving it multiple times has no event. It only resolves it the first time. After that, any time it returns that promise, it will immediately fire the .then, cause the deferred is already resolved.
myService.get = function(){
var deffered = $q.defer();
http://jsfiddle.net/8qLrnz5o/

Other solution is return the promise that $http creates (first put the callback and then return the promise) :
myService.get = function(requestParams) {
var call = $http({
method: 'GET',
url: TEST_DATA_URLS,
params: requestParams
});
call.success(function (msg) {
console.log('loading data - rest call finished');
});
return call;
};
when you call mySvs.get you have the same promise:
mySvc
.get({id: number})
.then(function() {
console.log('out put data for member ' + number);
});
Here is the fiddle:
http://jsfiddle.net/tmbs0b1L/

Related

Make Angular $http service process results one after another

I have a very large angularjs app, that sells stuff and has filters
It seems that we need to support people on flaky connection.
That means that if user selects 'used product' filter and then he unselects 'used product', there will be a 2 calls to the server via $http.
$http.get("reloadresults?used=true", function (response) { $scope.items = response items; }); at 12:03:04 hours
$http.get("reloadresults?used=false", function (response) { $scope.items = response items; }); at 12:03:05
Now, image there is a bottleneck or something and the first call with 'used=true' returns last, then there is a problem with the filters.
I know there is a $http interceptor in angularjs, based on promises, how would i fix this problem? So that requests are processed in the order they are sent, meaning 'used=true' and only then used=false.
Edit: cant block thread, cant refactor, just need for the promises to fullfil in the order they were first sent. I think ill post answer later.
I din't understand your question well but i think you are looking for
$q.all(valid_request)
You could indeed ensure that success handlers are called in the correct order by forming a queue (a promise chain) however it is simpler, and better in this case, to nullify the previous request each time a new request is made.
There's a number of ways in which this could be achieved. Here's one ...
function cancelPrevious(fn) {
var reject_ = null;
return function(x) {
if(reject_) reject_(new Error('cancelled'));
return Promise.race(fn(x), new Promise(function(_, reject) {
reject_ = reject; // if reject_ is called before fn(x) settles, the returned promise will be rejected.
}));
};
}
which allows you to write ...
var get = cancelPrevious(function(str) {
return $http.get(str);
});
... and to make requests from any number of event threads :
get('reloadresults?used=true').then(function(response) {
// This function will be reached only if
// $http.get('reloadresults?used=true') fulfills
// before another get() call is made.
$scope.items = response.items;
});
...
// This call causes the then callback above to be suppressed if not already called
get('reloadresults?used=false').then(function(response) {
$scope.items = response.items;
});
Notes :
http requests are not canceled. Instead, the succuss path stemming from each request is made "cancelable" by racing against a rejectable promise.
side-effects included in the function passed to cancelPrevious() may be executed; in general, don't include such side effects.

Unable to figure out what's happening here in $http.get - Angular

I'm a newbie to Angular JS. I was referring Online tutorials and came across the $http service.
(function () {
"use strict";
angular
.module("ngClassifieds") // referring module which is already created
.controller("classifiedsCtrl", function ($scope, $http) {
$http.get('data/classifieds.json')
.then(function(classifieds){
$scope.classifieds = classifieds.data;
})
});
})();
In this piece of code, I'm not able to figure out these lines. Can
anyone explain what actually happens here ?
$http.get('data/classifieds.json')
.then(function(classifieds){
$scope.classifieds = classifieds.data;
}
I have this data in my data/classifieds.json file.
My question is, what exactly the data referred in classifieds.data is ?
what does classifieds.data represent here ?
what information does it contain?
what would be the result which we assign to $scope.classifieds?
$http returns a promise, it's an asynchronous call, angular use a fork of a library called Q for promises, you can see $q documentation here: https://docs.angularjs.org/api/ng/service/$q.
When the promise is fulfilled, that means, the asynchronous call is complete, the .then method call success or error callback depending on the result of the async call.
.then(successCallback, [errorCallback], [notifyCallback]) – regardless
of when the promise was or will be resolved or rejected, then calls
one of the success or error callbacks asynchronously as soon as the
result is available. The callbacks are called with a single argument:
the result or rejection reason. Additionally, the notify callback may
be called zero or more times to provide a progress indication, before
the promise is resolved or rejected.
The argument passed to the success callback is an object with the information about the request response. The data property contain the body of the response, in other way, all the content of data/classifieds.json file (in your case), therefore, $scope.classifieds will contain the json returned by data/classifieds.json.
Here a friendly article about promises and $q: http://www.dwmkerr.com/promises-in-angularjs-the-definitive-guide/
A simple get request example from AngularJS Docs.
$http({
method: 'GET',
url: '/someUrl'
}).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
In your code,
$http.get('data/classifieds.json') // This is the URI of your json file
.then(function(classifieds){ // Success callback
$scope.classifieds = classifieds.data; // Here, you are assigning response's data into $scope.classifieds
}

Don't execute a $resource request in angular when there is already a request running

I use a interval of 10 seconds for sending a request to get the most recent data:
var pollInterval = 10000;
var poll;
poll= $interval(function()
{
getNewestData();//$resource factory to get server data
}, pollInterval );
This works fine voor 99% of the time, but if the internet speed is really slow(I have actually experienced this), It will send the next request before the current is finished. Is there a way to just skip the current interval request if the previous one is still busy? Obsiously I could just use booleans to keep the state of the request, but I wonder if there is a better(native to angular) way of doing this?
Use the $resolved property of the Resource object to check if the previous operation is done.
From the Docs:
The Resource instances and collections have these additional properties:
$promise: the promise of the original server interaction that created this instance or collection.
$resolved: true after first server interaction is completed (either with success or rejection), false before that. Knowing if the Resource has been resolved is useful in data-binding.
$cancelRequest: If there is a cancellable, pending request related to the instance or collection, calling this method will abort the request.
-- AngularJS ngResource $resource API Reference.
How about making the request, then waiting for that to complete and then wait 10 seconds before making the same request again? Something along this line:
var pollInterval = 10000;
var getNewestData = function () {
// returns data in promise using $http, $resource or some other way
};
var getNewestDataContinuously = function () {
getNewestData().then(function (data) {
// do something with the data
$timeout(function () {
getNewestDataContinuously();
}, pollInterval);
});
};
getNewestData is the function that actually makes the request and returns the data in a promise.
And once data is fetched, a $timeout is started with timer as 10 seconds which then repeats the process.

$scope variable not set from Factory

I'm new to Angular
I'm trying to pass some data into a controller from a factory method. I can see the the data when I log the Factory variable. I try to pass the data to a $scope variable ($scope.song) but outside of the method, the $scope variable is undefined. What am I doing wrong? Code Below:
.controller('MessagesCtrl', function($scope, FetchSong){
FetchSong.nextSong('audio-message')
.then(function(response){
$scope.song = FetchSong.queue;
console.log(FetchSong.queue); //logs the data
console.log(response.data); //also logs the data
});
console.log($scope.song); //returns undefined
})
Here is the timeline of the execution of the code:
// t0: ask the service for the next song: the service sends an HTTP
// request (I guess) to get it
FetchSong.nextSong('audio-message')
.then(function(response){
// t0 + 3 seconds: the http message has reached the server 10,000
// miles away from here, the server got the next song from its
// database, and sent it back. It took some time to travel the 10,000
// miles in the other direction, but it finally arrived, so we can
// store it in the scope
$scope.song = FetchSong.queue;
console.log(FetchSong.queue); //logs the data
console.log(response.data); //also logs the data
});
// t0 + 1 microsecond: try to print the next song
console.log($scope.song); //returns undefined
The key thing to realize is that, every time a service returns a promise on which you call then() and pass a callback function, it means it can't just return the value now. It returns... a promise that will be resolved later, because some work needs to be done asynchronously before the result can be available.
So, printing the result immediately after you called the service and got the promise back will never work. The result is only available once the callback function has been called, later.
I have written a blog post explaining how promises work and how to avoid traps like the ones you fell into.
The issue is, you try to access $scope.song before it been assigned value in FetchSong.nextSong promise callback, since promise is asynchronous, all code related to a promise return data should be put in its callback, see doc:
.controller('MessagesCtrl', function($scope, FetchSong){
FetchSong.nextSong('audio-message').then(function(response){
$scope.song = FetchSong.queue;
console.log(FetchSong.queue); //logs the data
console.log(response.data); //also logs the data
}).then(function(){
console.log($scope.song); //returns FetchSong.queue
});
})
you should use $scope.$apply(); see more in https://docs.angularjs.org/api/ng/type/$rootScope.Scope

Multiple requests, same response

I'm facing this strange issue with the $http service: every request made to the API provided on the company that I work, wrapped in a $q.all(), return the same response.
var promises = [
$httpPromiseA(),
$httpPromiseB(),
$httpPromiseC()
];
$q.all(promises)
.then(function (response) {
$log.log(response);
});
// $log.log returns [expectedObjectFromA, expectedObjectFromA, expectedObjectFromA]
This occurs randomly: the expectedObjectFromA can be expectedObjectFromB or expectedObjectFromC. The fact is that all of the three objects return the same, or two of one and one of other, etc.
If I chain one after other, in a linear manner, they all work perfectly, but takes more time to acomplish the task, of course.
var def = $q.deferred();
var resolvedData = [];
$httpPromiseA()
.then(function (response) {
$log.log(response);
resolvedData.push(reponse);
return $httpPromiseB();
})
.then(function (response) {
$log.log(response);
resolvedData.push(reponse);
return $httpPromiseC();
})
.then(function (response) {
$log.log(response);
resolvedData.push(reponse);
return def.resolve(resolvedData);
});
return def.promise();
// $log.log returns [expectedObjectFromA],
// [expectedObjectFromB] and [expectedObjectFromC]
// as expected. ResolvedData brings [expectedObjectFromA,
// expectedObjectFromB, expectedObjectFromC].
Can you give me some directions on what may be happening here?
I'm using the core implementations of $http and $q. I've tried one $q.allSettled, applied as $decorator, based on the API resource of Kris Kowalk's Q, but didn't succeed too.
Thanks.
EDIT 1:
I cannot pass the arguments to the functions separately because it's a wrapper function thats call the $http service, here in my app. The wrapper function expects a String as the first argument, and an Object as the second. The wrapper function returns the $http calls.
EDIT 2:
This Plunker calls 2 concurrent requests, one to the Instagram API and the other to the Flickr API. The problem don't occur is this Plunker. I really don't know how to deal with this trouble. This is REALLY annoying.
In $q.all you'll want to pass in an array of promises, instead executing the function. $httpPromiseA.$promise instead of $httpPromiseA() and so on.
The problem was on the server side, which was having problems with simultaneous requests, answering all made together with the same response.
Thanks for all support and attention.

Resources