AngularJS two http get in one controller make problems - javascript

i have two http GET in one controller and sometimes it works and two of them are working. sometime only one http Get is work. and sometimes none of them is shown.
any suggestions?
}).controller("nextSidorAdminCtrl",
function($scope,$rootScope,$http,$location,$state) {
$http.get("/ShiftWeb/rest/admin/getallsettingtime")
.then(function(response) {
$scope.settingtimes = response.data;
});
$http.get("/ShiftWeb/rest/admin/nextsidor")
.then(function(response) {
$scope.nextsidor = response.data;
});
Image:
https://prnt.sc/k5ewd6

Chain the two $http.get operations:
}).controller("nextSidorAdminCtrl",
function($scope,$rootScope,$http,$location,$state) {
$http.get("/ShiftWeb/rest/admin/getallsettingtime")
.then(function(response) {
$scope.settingtimes = response.data;
return $http.get("/ShiftWeb/rest/admin/nextsidor")
})
.then(function(response) {
$scope.nextsidor = response.data;
});
Because calling the .then method of a promise returns a new derived promise, it is easily possible to create a chain of promises. It is possible to create chains of any length and since a promise can be resolved with another promise (which will defer its resolution further), it is possible to pause/defer resolution of the promises at any point in the chain.
For more information, see AngularJS $q Service API Reference - chaining promises

The best way to solve this problem is to use async
In my openion, The problem in Mr. georgeawg's answer is, if $http.get("/ShiftWeb/rest/admin/getallsettingtime") will return success then $http.get("/ShiftWeb/rest/admin/nextsidor") will be called, Otherwise It will not be called.
And as I can see in the question both are independent.
So you need to follow the best way which is used async or something like that.
So your code will be :
var getAllAettingTime = function(cb) {
$http.get("/ShiftWeb/rest/admin/getallsettingtime")
.then(function(response) {
if(response.data){
$scope.settingtimes = response.data;
return cb(null,'OK');
}else{
return cb(null,'ERROR');
})
}
var nextSidor= function(cb) {
$http.get("/ShiftWeb/rest/admin/nextsidor")
.then(function(response) {
if(response.data){
$scope.nextsidor = response.data;
return cb(null,'OK');
}else{
return cb(null,'ERROR');
})
}
async.series([ getAllAettingTime, nextSidor], function(err, result) {
if (err){
/* Do what you want if err comes */
} else {
/* Do what you want if both HTTP come with success */
}
});
In the above async.series() both HTTP will be called without any dependency on each other.
For better understanding, you need study about async npm module and have to install in your app.

Related

Angular async function returns undefined instead of promise

I am working with an Angular controller that will add an undetermined amount of questions from one or more surveys uploaded to the server to an existing survey(s) with same name, that's just for understanding, the uploading, handling in back-end and returning response works flawlessly, the real problem here is the specific part of just uploading the new questions, which has the following functions:
//Will save next question
const next_question = function (response, this_survey, response_survey, sur_questions, question) {
deferred = $q.defer();
new_question = new QuestionsService();
//Does a bunch of stuff with question object using all those arguments,
//which is not relevant here
if (check_existing_question(new_question, sur_questions)) {
deferred.resolve();
}
else {
new_question.$save({
actsern: new_question.actsern
}).then(function () {
deferred.resolve();
}).catch(function () {
deferred.reject();
});
}
return deferred.promise;
};
// Save the questions synchronously (wait for a promise to resolve/reject before saving next question)
async function save_question(response, this_survey, response_survey, sur_questions) {
// I want to store all promises in an array and return for future error handling
promises = [];
for (const quest in response_survey) {
// promise is undefined in console.log!!
promise = await next_question(response, this_survey, response_survey, sur_questions, quest);
console.log(promise);
//Commented because if not returns error "promises.push is not a function"
//promises.push(promise);
}
//Still undefined
console.log(promise);
return promise;
//The above is how I managed to make it work but what I really want is:
//return promises;
}
const set_survey_questions = function(response, this_survey, response_survey){
//Never mind this, unrelated to my problem
let sur_questions = QuestionsService.get({
//this_survey is survey object that is being uploaded to, questions and surveys are linked through actsern
actsern: this_survey.actsern
});
sur_questions.$promise.then(function () {
promises = save_question(response, this_survey, response_survey, sur_questions);
$q.all(promises).then(function () {
console.log("Uploaded successfully all questions");
}).catch(function (reason) {
console.log(reason);
});
});
};
The thing is that I have to save the questions synchronously, wait for one to resolve/reject before saving the next, which is why I am using an async loop function. Everything works fine, the questions are uploaded one after the order, everything goes to the server exactly as it's supposed to. The problem is that I would like to get the promises of next_question() from the async function to deal with their errors in the future and do some other stuff after they all have been resolved using the $q.all(), but the problem is that as far as I know the await should return a promise but it just returns undefined and keeps as undefined even after the loop has finished and all promises, supposedly, resolve.
I know the function next_question() works fine because if it is called directly (outside of async function, directly from set_survey_questions()) it saves the question and returns the promise just as it's supposed.
I'm not very experienced with angular or even javascript for that matter so any help or improvement you can think of is welcome.
As far as I know the await should return a promise but it just returns undefined
No. You should pass a promise to the await operator, it will then block execution of the async function until the promise is settled and return the result value of the promise.
The problem is that I would like to get the promises of next_question() from the async function to deal with their errors in the future
That doesn't seem to be compatible with your requirement of sequential saving. There are no multiple promises, there's only one active at a time. If any of them errors, the await will throw an exception and your loop will stop.
I think you really should simplify to
async function set_survey_questions(response, this_survey, response_survey) {
let sur_questions = QuestionsService.get({
actsern: this_survey.actsern
});
await sur_questions.$promise;
try {
for (const quest in response_survey) {
await next_question(response, this_survey, response_survey, sur_questions, quest);
}
console.log("Uploaded successfully all questions");
} catch (reason) {
console.log(reason);
}
}

Resolving chained Angular Promises synchronously [duplicate]

This question already has an answer here:
How to chain and share prior results with Promises [duplicate]
(1 answer)
Closed 5 years ago.
I have a resolve promise function that uses $q service, where there is some generic code to resolve/reject based on certain conditions. I have a scenario wherein I would have to execute api2 only after api1 is successfully resolved. But both the calls are happening asynchronously. I have pasted the pseudo code below. Please help. Thanks a lot in advance.
var resolvePromise = function(promise)
{
var defer = $q.defer();
promise.then(function(response)
if(certain conditions are true)
{
defer.reject(err)
}
defer.resolve();
)
.catch(function(errors){
defer.reject(errors);
})
return defer.promise;
}
function synchronousCalls()
{
var promise1 = service.getApi1();
var promise2 = service.getApi2();
return resolvePromise(promise1).then(function(){
return resolvePromise(promise2);
})
}
function getData()
{
synchronousCalls().then(function(){
console.log("synchronous run of apis ended");
})
}
You don't need the resolvePromise function. Have getApi1 and getApi2 return Promises directly that you can .then(). Additionally, calls to functions that return Promises don't stop execution context. You are firing calls to both APIs immediately and not waiting for the first to finish. You need to call to getApi1(), and .then() the Promise that should be returned from it. Consider the following code:
// This says, fire them both immediately
var promise1 = service.getApi1();
var promise2 = service.getApi2();
// Your call should look something like this
service
.getApi1()
.then(function (api1Response) {
// If I'm in here, I know that the request to service 1 is done
return service
.getApi2();
}).then(function (api2Response) {
// If I'm in here, I know that the request to service 2 is done
console.log(api2Response);
})
.catch(function (err) {
console.log("Something has gone wrong");
});
Both of you getApi functions should look something like this, where the main thing they do is return something (like a Promise) with a .then() method.
function getApi1() {
// This is returning a Promise
return $http
.get("some/resource/on/the/server.json")
.then(function (response) {
/*
* Here I'm just peeling out the data from the response
* because I don't actually care about the details of the
* response, just the data
*/
return response.data;
});
}
Change the synchronousCalls to
function synchronousCalls()
{
return service.getApi1().then(resolvePromise).then(service.getApi2).then(resolvePromise);
}

Angular not getting data from FS.readFile with promises

I am trying to use an Angular service to make a call to either use fs.readFile or fs.writeFile depending on type of button pressed in order to understand how node and angular promises interact. What I have is reading writing files, but does not send back read data, nor does it throw any errors for me to understand what has gone wrong.
//HTML
<button ng-click="rw('write')">WRITE FILE</button>
<button ng-click="rw('read')">READ FILE</button>
//angular
angular.module('test', [])
.controller('ctrl', function($scope, RWService){
$scope.rw = function(type){
RWService.rw(type)
.then(
function(res){
console.log('success');
},
function(err){
console.log('error');
})
};
})
.service('RWService',['$http', '$q', function($http, $q){
this.rw = function(type){
var promise = $http.get('./rw/' + type);
var dfd = $q.defer();
promise.then(
function(successResponse){
dfd.resolve(successResponse);
},
function(errorResponse){
dfd.reject(errorResponse);
}
);
return dfd.promise;
};
}]);
//node
var fs = require('fs')
, async = require('async')
, Q = require('Q');
var dest = './file.txt';
var rw = {
write: function(data){
data = data.repeat(5);
return Q.nfcall(fs.writeFile, dest, data);
}
, read: function(data){
data = data.repeat(5);
var deferred = Q.defer();
console.log('inside read');
fs.readFile(dest, 'utf8', function(err, data){
if (err){
deferred.reject('some error');
}else{
deferred.resolve(data);
}
});
return deferred.promise;
}
};
module.exports = exports = rw;
//node server
app.get('/rw/:type', function(req, res, next){
var type = req.params.type;
var data = 'some text string\n';
if (type == 'write'){
//omitted fro brevity
}else{
rw.read(data)
.then(function(response){
return {'response': response};
})
.catch(function(err){
return {'index.js error': err};
});
}
});
I structured the angular $q portion off of this blog post.
Here is a native Promise implementation of your code.
var fs = require('fs');
var dest = './file.txt';
var rw = {
write: function(data){
return new Promise(function (resolve, reject) {
data = data.repeat(5);
fs.writeFile(function (err, result) {
if (err) return reject(err.message);
return resolve(result);
});
});
},
read: function(data){
return new Promise(function (resolve, reject) {
data = data.repeat(5);
fs.readFile(dest, 'utf8', function(err, contents) {
if (err) return reject(err.message);
return resolve(contents.toString());
});
});
}
};
module.exports = exports = rw;
[edit: I just changed the code to put data=data.repeat(5) inside the promise factory method. Basically, if anything CAN raise an exception, you should try to put it inside that promise function, or you again run the risk of silently killing the script again.]
A couple of comments:
Returning deferred is incredibly useful, but you have to be careful about how you use it. I personally only use it if the asynchronous code cannot be wrapped in a simple function (such as a class instance that creates a promise in its constructor and resolves/rejects in different child methods). In your case, probably what is happening is that the script is failing in a way that fs.readFile() never gets called -- and so deferred.resolve() and deferred.reject() will never be reached. In cases like this, you need to use try/catch and always call deferred.reject() in there as well. It is a lot of extra work that is easily avoided.
Instead, you should try to use the vanilla standard implementation of Promises as you see above.
Lastly, Q was a groundbreaking library that basically taught the world how to do promises in the first place, but it has not been updated in years and was never particularly feature-rich or fast. If you need more features, take a look at when.js*, kew or Bluebird (note that Bluebird claims to be the fastest, but I've personally found that to be untrue.)
(*I actually loved working with when.js and find it a bit painful using dumb native promises, but hey, standards are standards.)
[edit: Adding details on the Angular side of things]
So based on your comment, here is what I suspect you are also looking for. You will see that here I am using $http.get() as the only promise. No need to use defer() once you are inside a promise, so actually there is no need to even include $q.
I'm sorry, I've never used service(). Even Angular's own documentation on creating services uses the factory() method, so that's what I'm using here.
.factory('RWService',['$http', function($http){
return {
rw: function (type) {
// $http returns a promise. No need to create a new one.
return $http.get('./rw/' + type)
.then(function (response) {
// You can do other stuff here. Here, I am returning the
// contents of the response. You could do other stuff as
// well. But you could also just omit this `then()` and
// it would be the same as returning just the response.
return response.data;
})
.catch(function (err) {
// You can do other stuff here to handle the error.
// Here I am rethrowing the error, which is exactly the
// same as not having a catch() statement at all.
throw err;
});
}
};
}]);
If you read the comments in the code above, you should realize that you can write the same code like this:
.factory('RWService',['$http', function($http){
return {
rw: function (type) {
return $http.get('./rw/' + type);
}
};
});
The only difference here is that RWService.rw() will eventually resolve the entire response object rather than the response data.
The thing to keep in mind here is that you can (and absolutely should) try to recycle your promises as much as possible. Basically, all you need to know about promises is:
every promise has a then and catch method you can wrap your logic in;
every then and catch return as a new promise;
if you throw an exception inside any then or catch, you will be thrown straight to the next catch, if there is one;
if you return a value from any then or catch, it will be passed as the argument to the very next then in the chain;
when the chain runs out of thens or you throw an exception and there are no more catches, the promise chain ends; and
then and catch are fast, but they are still asynchronous, so don't add new elements to a promise chain if you don't genuinely need them.

How to wait for AsyncRequests for each element from an array using reduce and promises

I'm new on AngularJS and JavaScript.
I am getting remote information for each of the elements of an array (cars) and creating a new array (interested prospects). So I need to sync the requests. I need the responses of each request to be added in the new array in the same order of the cars.
I did it first in with a for:
for (a in cars) {
//async request
.then(function () {
//update the new array
});
}
This make all the requests but naturally didn't update the new array.
After seeking in forums, I found this great examples and explanations for returning a intermediate promise and sync all of them.
1. http://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html
2. http://stackoverflow.com/questions/25605215/return-a-promise-from-inside-a-for-loop
3. http://www.html5rocks.com/en/tutorials/es6/promises/
(#MaurizioIndenmark, #Mark Rajcok , #Michelle Tilley, #Nolan Lawson)
I couldn't use the Promise.resolve() suggested in the second reference. So I had used $q.defer() and resolve(). I guess I have to inject a dependency or something else that I missed. As shown below:
In the Controller I have:
$scope.interestedProspects = [] ;
RequestDetailsOfAsync = function ($scope) {
var deferred = $q.defer();
var id = carLists.map(function (car) {
return car.id;
}).reduce(function (previousValue, currentValue) {
return previousValue.then(function () {
TheService.AsyncRequest(currentValue).then(function (rData) {
$scope.interestedProspects.push(rData);
});
});
}, deferred.resolve());
};
In the Service I have something like:
angular.module('app', []).factory('TheService', function ($http) {
return {
AsyncRequest = function (keyID) {
var deferred = $q.defer();
var promise = authorized.get("somep.provider.api/theService.json?" + keyID).done(function (data) {
deferred.resolve(data);
}).fail(function (err) {
deferred.reject(err);
});
return deferred.promise;
}
}
}
The displayed error I got: Uncaught TypeError: previousValue.then is not a function
I made a jsfiddle reusing others available, so that it could be easier to solve this http://jsfiddle.net/alisatest/pf31g36y/3/. How to wait for AsyncRequests for each element from an array using reduce and promises
I don't know if the mistakes are:
the place where the resolve is placed in the controller function.
the way the reduce function is used
The previousValue and currentValue sometimes are seen by javascript like type of Promise initially and then as a number. In the jsfiddle I have a working example of the use of the reduce and an http request for the example.
Look at this pattern for what you want to do:
cars.reduce(function(promise, car) {
return promise.then(function(){
return TheService.AsyncRequest(car).then(function (rData) {
$scope.details.push(rData);
});
});
}, $q.when());
This will do all the asynchronous calls for every car exactly in the sequence they are in the cars array. $q.all may also be sufficient if the order the async calls are made doesn't matter.
It seems you are calling reduce on an array of ids, but assume in the passed function that you are dealing with promises.
In general, when you want to sync a set of promises, you can use $q.all
You pass an array of promises and get another promise in return that will be resolved with an array of results.

Angular promise on multiple $http

I am trying to do multiple $http call and my code looks something like this:
var data = ["data1","data2","data3"..."data10"];
for(var i=0;i<data.length;i++){
$http.get("http://example.com/"+data[i]).success(function(data){
console.log("success");
}).error(function(){
console.log("error");
});
}
How can I have the promise to know all $http call is successfull? If anyone of it fail, will perform some action.
You could also use $q.all() method.
So, from your code:
var data = ["data1","data2","data3"..."data10"];
for(var i=0;i<data.length;i++){
$http.get("http://example.com/"+data[i]).success(function(data){
console.log("success");
}).error(function(){
console.log("error");
});
}
You could do:
var promises = [];
data.forEach(function(d) {
promises.push($http.get('/example.com/' + d))
});
$q.all(promises).then(function(results){
results.forEach(function(data,status,headers,config){
console.log(data,status,headers,config);
})
}),
This above basically means execute whole requests and set the behaviour when all have got completed.
On previous comment:
Using status you could get to know if any have gone wrong. Also you could set up a different config for each request if needed (maybe timeouts, for example).
If anyone of it fail, will perform some action.
From docs which are also based on A+ specs:
$q.all(successCallback, errorCallback, notifyCallback);
If you are looking to break out on the first error then you need to make your for loop synchronous like here: Angular synchronous http loop to update progress bar
var data = ["data1", "data2", "data3", "data10"];
$scope.doneLoading = false;
var promise = $q.all(null);
angular.forEach(data, function(url){
promise = promise.then(function(){
return $http.get("http://example.com/" + data[i])
.then(function (response) {
$scope.data = response.data;
})
.catch(function (response) {
$scope.error = response.status;
});
});
});
promise.then(function(){
//This is run after all of your HTTP requests are done
$scope.doneLoading = true;
});
If you want it to be asynchronous then: How to bundle Angular $http.get() calls?
app.controller("AppCtrl", function ($scope, $http, $q) {
var data = ["data1", "data2", "data3", "data10"];
$q.all([
for(var i = 0;i < data.length;i++) {
$http.get("http://example.com/" + data[i])
.then(function (response) {
$scope.data= response.data;
})
.catch(function (response) {
console.error('dataerror', response.status, response.data);
break;
})
.finally(function () {
console.log("finally finished data");
});
}
]).
then(function (results) {
/* your logic here */
});
};
This article is pretty good as well: http://chariotsolutions.com/blog/post/angularjs-corner-using-promises-q-handle-asynchronous-calls/
Accepted answer is okay, but is still a bit ugly. You have an array of things you want to send.. instead of using a for loop, why not use Array.prototype.map?
var data = ["data1","data2","data3"..."data10"];
for(var i=0;i<data.length;i++){
$http.get("http://example.com/"+data[i]).success(function(data){
console.log("success");
}).error(function(){
console.log("error");
});
}
This becomes
var data = ['data1', 'data2', 'data3', ...., 'data10']
var promises = data.map(function(datum) {
return $http.get('http://example.com/' + datum)
})
var taskCompletion = $q.all(promises)
// Usually, you would want to return taskCompletion at this point,
// but for sake of example
taskCompletion.then(function(responses) {
responses.forEach(function(response) {
console.log(response)
})
})
This uses a higher order function so you don't have to use a for loop, looks a lot easier on the eyes as well. Otherwise, it behaves the same as the other examples posted, so this is a purely aesthetical change.
One word of warning on success vs error - success and error are more like callbacks and are warnings that you don't know how a promise works / aren't using it correctly. Promises then and catch will chain and return a new promise encapsulating the chain thus far, which is very beneficial. In addition, using success and error (anywhere else other than the call site of $http) is a smell, because it means you're relying explicitly on a Angular HTTP promise rather than any A+ compliant promise.
In other words, try not to use success/error - there is rarely a reason for them and they almost always indicate a code smell because they introduce side effects.
With regards to your comment:
I have did my own very simple experiment on $q.all. But it only trigger when all request is success. If one if it fail, nothing happen.
This is because the contract of all is that it either resolves if every promise was a success, or rejects if at least one was a failure.
Unfortunately, Angular's built in $q service only has all; if you want to have rejected promises not cause the resultant promise to reject, then you will need to use allSettled, which is present in most major promise libraries (like Bluebird and the original Q by kriskowal). The other alternative is to roll your own (but I would suggest Bluebird).

Categories