Calling second request after first one is finished - javascript

I have a question about requests.I make first request then I suppose first onSuccess method will be runnning but program make second request immediately.How can i handle it? Context.getAlarmGroupById is called 2 times immediately.
my code:
function loadAll () {
Context.getAlarmGroupChainById({
id: $stateParams.id
}, onSuccess, onError);
function onSuccess(data, headers) {
vm.temp=data;
var numberGroupChain=vm.temp.length;
for(var i=0; i<numberGroupChain; i++){
vm.location.push(vm.temp[i].location);
vm.alarmChainList.push({name: vm.location[i],active:null,totalAlarmGroupNumber:null});
//loadData(vm.temp[i].id);
asyncLoop(vm.temp[i].id);
}
}
function onError(error) {
AlertService.error(error.data.message);
}
var index = 0;
function asyncLoop(chainId) {
if(index >= vm.temp.length) {
return;
}
Context.getAlarmGroupById({
id:chainId
}, onSuccess, onError);
function onSuccess(data,headers){
index++;
asyncLoop();
}
function onError(data,headers){
index++;
asyncLoop();
}
}
}

Firstly: Car.getGroupId is an asynchronous function. So you have to ensure that the previous call is completed, before the next call.
Secondly: The recursive function is created for replacing the loop, after the success function and index increment, the recursive function is called to ensure your required requirement.
Third: Change the calling sequence of asyncLoop(vm.temp[i].id); after loop:
Finally:
Please use the following code:
function loadAll () {
var index = 0;
function asyncLoop(chainId) {
if(index >= vm.temp.length) {
return;
}
Context.getAlarmGroupById({
id:vm.temp[index].id
}, onSuccess, onError);
function onSuccess(data,headers){
index++;
asyncLoop();
}
function onError(data,headers){
index++;
asyncLoop();
}
}
function onSuccessParent(data, headers) {
vm.temp=data;
var numberGroupChain=vm.temp.length;
for(var i=0; i<numberGroupChain; i++){
vm.location.push(vm.temp[i].location);
vm.alarmChainList.push({name: vm.location[i],active:null,totalAlarmGroupNumber:null});
//loadData(vm.temp[i].id);
}
asyncLoop();
}
function onErrorParent(error) {
AlertService.error(error.data.message);
}
Context.getAlarmGroupChainById({
id: $stateParams.id
}, onSuccessParent, onErrorParent);
}

Here is the pure JavaScript way to do this and it works perfectly fine. Use Promise.then(). Do like below:
var request1 = new Promise(function(resolve, reject) {
resolve('Success!');
});
request1.then(function(value) {
// make request2 here
});

Related

Use callback with javascript

I am creating project using javascript and nodejs. I am integrating callback in my function inside for loop with condition basis,but am unable to do this.my problem is callback is completed on first iteration of loop. here is my code:
function tagEndpointNames(callback) {
var data = userGenerateToken();
var sql = "SELECT * FROM topology_data WHERE topology_coordinates !='' and topology_uuid is not null"
var query = conn.query(sql, function(err, tagEndpointNames) {
for (var i = 0; i < tagEndpointNames.length; i++) {
var topologytagData = {
"topology_tag": tagEndpointNames[i].topology_uuid
}
var tpCooridinates = JSON.parse(tagEndpointNames[i].topology_coordinates);
for (var j = 0; j < tpCooridinates.stageObjects.length; j++) {
if (tpCooridinates.stageObjects.length) {
if (tpCooridinates.stageObjects[j].endPointId) {
if (isGuid(tpCooridinates.stageObjects[j].endPointId)) {
var endPointUUID = tpCooridinates.stageObjects[j].endPointId;
var _ro = require('request');
var url = url;
var _d = '';
_ro({
url: url,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + data['access_token']
},
json: topologytagData
}, function(_e, _r, _b) {
if (_r.statusCode == 200 && !_e) {
callback()
//return;
} else {
callback()
console.log("andarss")
return;
}
})
}
}
}
}
}
})
}
Here is the function call:
tagEndpointNames(function(){
console.log ('Server Closed during MIGRATION JOB 4');
server.close(function () {
process.exit(0);
});
})
When you are running asynchronous process with callback in a for loop, remember that the callback from callee will be fired in the first event completed inside the loop. In your case request lib call is an asynchronous process inside for loop, you need to handle all callback from all the request call before you want to callback the callee.
Please read:
How to write asynchronous functions for Node.js
Maybe it's time for you to start using Javascript Promise.
The async library for Node will help you for doing this kind of tasks.
Use async waterfall.It Runs an array of functions in series, each passing their results to the next in the array. However, if any of the functions pass an error to the callback, the next function is not executed and the main callback is immediately called with the error.
js
var create = function (req, res) {
async.waterfall([
_function1(req),
_function2,
_function3
], function (error, success) {
if (error) { alert('Something is wrong!'); }
return alert('Done!');
});
};
function _function1 (req) {
return function (callback) {
var something = req.body;
callback (null, something);
}
}
function _function2 (something, callback) {
return function (callback) {
var somethingelse = function () { // do something here };
callback (err, somethingelse);
}
}
function _function3 (something, callback) {
return function (callback) {
var somethingmore = function () { // do something here };
callback (err, somethingmore);
}
}
Reference

Javascript for loop Promises

I have an array of urls like this
var urls = ["www.google.com", "www.yahoo.com"];
And I want to loop though the urls and perform an async task inside the loop and not move on to the next item until the async task has finished. I know you can do this with promises but I have having some trouble with it. Here what I have
var xmlReader = require('cloud/xmlreader.js');
function readResponse_async(xlmString) {
var promise = new Parse.Promise();
xmlReader.read(xlmString, function (err, res) {
if(err) {
promise.reject(err);
} else {
promise.resolve(res);
}
});
return promise;
}
for (i = 0; i < urls.length; i++) {
Parse.Cloud.httpRequest({
url: unionUrls[i],
}).then(function(httpResponse) {
try {
// console.log(httpResponse.text)
return readResponse_async(httpResponse.text)
} catch (e) {console.log(e)}
}
But right now it doesn't wait for the readResponse_async to finish, how can I have it wait for that?
Thanks
EDIT
After reading the response I make a save to my database and I have another array like this
var location = ['USA', 'England'];
And I make the save like this
function saveLoc_async(data, location) {
var i3, i4, i5, m,
TestItem = Parse.Object.extend("TestItem"),//can be reused within the loops?
promise = Parse.Promise.as();//resolved promise to start a long .then() chain
for (i3 = 0; i3 < data.count(); i3++) {
(function(testItem) {
testItem.set("item", data.at(i));
testItem.set("location", location);
//build the .then() chain
promise = promise.then(function() {
return testItem.save();
});
})(new TestItem());
//************************
//CALL retry(); here?
//**************************
}
Because with your answer I have
function retry() {
if (urlsUnion.length > 0) {
var nextUrl = urlsUnion.pop();
//********** ADDED LINE
var nextLoc = location.pop();
Parse.Cloud.httpRequest({
url: nextUrl,
}).then(function(httpResponse) {
xmlReader.read(httpResponse.text, function (err, res) {
if(err) {
// show an error
} else {
//********** ADDED LINE
saveLoc_async(res, nextLoc);
retry();
}
});
});
}
}
SO where should retry(); go because right now with the save sometimes it puts the second location with one of the first items url? why would that happen?
I did something similar to this for an animation.
var actions = [drawXXX, fadeOutYYY, drawXYZ];
this.startAnimation = function () {
actions.reduce(function (previousAction, nextAction) {
return previousAction.then(nextAction)
}, $.when());
}
Your code fires both urls immediately, and does not wait in-between.
What you would have to do is to remove the first url from the array and fire it. In the 'then' branch check if you still have url's in the array and repeat.
Like this (untested, edited to make the code clean again):
var xmlReader = require('cloud/xmlreader.js');
function readResponse_async(xlmString) {
xmlReader.read(xlmString, function (err, res) {
if(err) {
// show an error
} else {
readFirstUrl();
}
});
}
function readFirstUrl() {
if (urlsUnion.length == 0) {
return;
}
var url = urlsUnion.pop();
Parse.Cloud.httpRequest({
url: url,
}).then(function(httpResponse) {
readResponse_async(httpResponse.text);
});
}
readFirstUrl();
Not sure I understand your use of unionUrls array, but if you have your URL's in a urls array, I think this is pretty clean:
function getUrl(url) {
return Parse.Cloud.httpRequest(url)
.then( function(httpResponse) {
return readResponse_async(httpResponse.text);
});
}
urls.reduce( function(prev, url) {
return prev ? prev.then( function() { getUrl(url); }) : getUrl(url);
}, null);

Return function value AFTER for-loop ends

I have the following function and I'm getting in the console false then true in that order. The true comes like 1-2 seconds after the false. I need the function to return false ONLY if no files were uploaded.
function uploadFeaturedImg()
{
var uploaded = false,
files = featuredImg.files;
if (files.length > 0)
{
for (var i = 0, len = files.length; i < len; i++)
{
var params = {
Key: 'tournament/image/'+files[i].name,
ContentType: files[i].type,
Body: files[i]
};
bucket.upload(params, function(err, data)
{
if (!err)
{
if (!uploaded) uploaded = true;
console.log(uploaded);
}
else
{
fileAlert(files[i].name);
}
});
}
}
console.log(uploaded);
return uploaded;
};
I ended up using another approach that worked for me better.
I'm also using .ajaxStop(): https://api.jquery.com/ajaxStop/. This basically lets me know when all files have been uploaded (if any).
There is an asynchrone problem here:
When you do your loop, you call for each iteration the function bucket.upload with a callback (it will be call when the action is finished). But your loop is ended when the last call yo bucket.upload is done, but NOT when all the callback are done.
So you return line is called BEFORE all the callback.
If you can understand that, you can also understand an asyncron function never return something (cause the function have to wait for something before end) but call a callback function when all it's done. (in this case the callback param)
To work fine you have to use a lib like async (Doc here)
Use it like that :
if (files.length > 0) {
async.each(
files,
function(file, cb) {
// This function will be called for each iteration
// ...
// Call your funciton here
bucket.upload(params, function(err, data) {
// When the callback is done, call the end for the iteration
cb();
}
},
function(err) {
// This function is called only when ALL callback are done.
callback(true); // All upload are done correctly
});
);
}
else {
// No files to upload
callback(false);
}
Well, it is always a bit complicated to deal with asynchronous code.
You should change your approach and pass a callback function in the signature of your method:
function uploadFeaturedImg(callback)
{
var uploaded = false,
files = featuredImg.files;
if (files.length > 0)
{
for (var i = 0, len = files.length; i < len; i++)
{
var params = {
Key: 'tournament/image/'+files[i].name,
ContentType: files[i].type,
Body: files[i]
};
bucket.upload(params, function(err, data)
{
if (!err)
{
if (!uploaded) uploaded = true;
if(callback) callback(); //Do what you were supposed to do with the result of your function in this callback function
}
else
{
fileAlert(files[i].name);
}
});
}
}
return uploaded;
};

NodeJS cannot create array with nested query

After a succesful query to Mongodb to get a list of news, for the news that have a link attached i search for link details in the database but after i set them inside the modified news object i cannot push it to an array. Why does this happen?
var newsarray = []
for(i=0;i<newsfound.length;i++){
if(!newsfound[i].link._id){
newsarray.push(newsfound[i])
} else {
var tempnew = newsfound[i];
db.findOne('links',{'_id':tempnew.link._id},function(err,linkdetails){
if(err){
console.log(err)
} else {
tempnew.linkdetails = linkdetails;
newsarray.push(tempnew)
}
})
}
}
console.log(newsarray)
The result of this is an array without the link containing news or if i try a few changes an array with the original news without the added details.
You can not use an asynchronous function inside a for loop. The reason is that the for loop gets executed before even any callback could come, and hence the scope gets messed up.
You should use recursive function (self calling) which will prevent the next async call before the previous is over.
var i = 0;
function fn() {
// your logic goes here,
// make an async function call or whatever. I have shown async in a timeout.
setTimeout(function () {
i += 1;
if (i < newsfound.length) {
fn();
} else {
// done
// print result
}
});
}
Update:
For your use case,
var newsarray = []
var i = 0;
function done() {
// done, use the result
console.log(newsarray);
}
function fn() {
if (!newsfound[i].link._id) {
newsarray.push(newsfound[i]);
i += 1;
if (i < newsfound.length) {
fn();
} else {
done();
}
} else {
var tempnew = newsfound[i];
db.findOne('links', {'_id':tempnew.link._id}, function(err, linkdetails){
if(err){
console.log(err)
} else {
tempnew.linkdetails = linkdetails;
newsarray.push(tempnew);
i += 1;
if (i < newsfound.length) {
fn();
} else {
done();
}
}
})
}
}

Handling multiple asynchronous requests in Azure?

For the reading a table script shown below, I am trying to run the query in the parameter and for each result trying to append a value from a different table to the result. But due to asynchronous nature of azure, request.respond() is always called before getInvites. Which means that the results are never appended with invites.
function read(query, user, request) {
request.execute({
success: function (results) {
for (var i = 0; i < results.length; i++) {
getInvites(results[i].id, function (invites) {
console.log("Assigning results.invites"); //runs second
results[i].invites = invites;
});
}
console.log("Request Responding"); //runs first
request.respond();
}
});
}
function getInvites(id, cb) {
var InvitesTable = tables.getTable("Invites").where({
"PlanID": id
}).select("UserID", "Attending");
InvitesTable.read({
success: function (results) {
if (cb) cb(results);
}
});
}
This is a followup question from this as I am unable to use external libraries in Azure. So how can I workaround the problem?
The asynchronous nature of the operations makes that a little challenging. What you need to do is to only call request.respond() once all the operations are done. I really miss the await keyword which I'm so fond from C#, but I've used an "asynchronous for loop" before, and it's worked quite well. Your code would look something like the one shown below:
function read(query, user, request) {
request.execute({
success: function (results) {
var index = 0;
var executeStep = function() {
if (index === results.length) {
// all invites have been retrieved
console.log("Request Responding");
request.respond();
} else {
getInvites(results[index].id, function(invites) {
console.log('Assigning results.invites');
results[i].invites = invites;
index++;
executeStep();
});
}
}
executeStep();
}
});
}
function getInvites(id, cb) {
var InvitesTable = tables.getTable("Invites").where({
"PlanID": id
}).select("UserID", "Attending");
InvitesTable.read({
success: function (results) {
if (cb) cb(results);
}
});
}
Ok you might do something like this
function read(query, user, request) {
request.execute({
success: function (results) {
for (var i = 0, len = results.length; i < len; i++) {
getInvites(results.id, function (invites) {
console.log("Assigning results.invites"); //runs second
results.invites = invites;
if (i === len-1) request.respond();
});
}
console.log("Request Responding"); //runs first
}
});
}
function getInvites(id, cb) {
var InvitesTable = tables.getTable("Invites").where({
"PlanID": id
}).select("UserID", "Attending");
InvitesTable.read({
success: function (results) {
if (cb) cb(results);
}
});
}
It basically means that request.respond() will be called at your successful callback of the last iteration.

Categories

Resources