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
Related
I am new to Nodejs and javascript and working on nodejs api code. I am using GandiAPI to check domainAvaliablity(Project related requirement) and have created a get request method (checkDomainAvaliablity) like this.
exports.checkDomainAvaliablity = function (req, res) {
gandiApi.methodCall('domain.available', [gandiapikey, [domain]], callback)
};
And I have a callback function which have 2 parameters(which I can not change).
I am able to get the value succesfully in my callback function.
Now I want to return "value" from callback and want to set in "res" paramameter of checkDomainAvaliablity function(Parent function) (something like res.json(task)) .
var callback = function (error, value) {
console.dir(value)
if (value[domain] == 'pending') {
console.log('result is not yet ready')
setTimeout(function () {
gandiApi.methodCall('domain.available', [gandiapikey, [domain]],
callback)
}, 700)
}
else {
console.dir(value)
}
// I want to return "value" from here and want to set in "res" paramameter of checkDomainAvaliablity function (Parent function).
}
Note: Use of callbackfuncion is neccessary.
Thanks #trincot. Putting the callback function inside the parent function works fine.
exports.checkDomainAvaliablity = function (req, res) {
domain = req.params.domainAvaliablity
var callback = function (error, value) {
console.log("logging" + value + error)
if (value[domain] == 'pending') {
console.log('result is not yet ready')
setTimeout(function () {
gandiApi.methodCall('domain.available', [gandiapikey, [domain]],
callback)
}, 700)
}
else {
res.send(value);
console.dir(value)
}
}
gandiApi.methodCall('domain.available', [gandiapikey, [domain]], callback)
};
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;
};
I'm looking to execute a callback upon the full completion of a recursive function that can go on for an undetermined amount of time. I'm struggling with async issues and was hoping to get some help here. The code, using the request module, is as follows:
var start = function(callback) {
request.get({
url: 'aaa.com'
}, function (error, response, body) {
var startingPlace = JSON.parse(body).id;
recurse(startingPlace, callback);
});
};
var recurse = function(startingPlace, callback) {
request.get({
url: 'bbb'
}, function(error, response, body) {
// store body somewhere outside these funtions
// make second request
request.get({
url: 'ccc'
}, function(error, response, body) {
var anArray = JSON.parse(body).stuff;
if (anArray) {
anArray.forEach(function(thing) {
request.get({
url: 'ddd'
}, function(error, response, body) {
var nextPlace = JSON.parse(body).place;
recurse(nextPlace);
});
})
}
});
});
callback();
};
start(function() {
// calls final function to print out results from storage that gets updated each recursive call
finalFunction();
});
It seems that once my code goes past the for loop in the nested requests, it continues out of the request and ends the initial function call while the recursive calls are still going on. I want it to not finish the highest-level iteration until all the nested recursive calls have completed (which I have no way of knowing how many there are).
Any help is GREATLY appreciated!
In your example you have no recursive calls. If I understand correctly you want to say that recurse(point, otherFunc); is the beginning of a recursive call.
Then just go back to the definition of the recursive call (which you have not shown in your post) and do this (add a third argument for a callback function to be called in the end of recursion; the caller will pass it as a parameter):
function recurse(startingPlace, otherFunc, callback_one) {
// code you may have ...
if (your_terminating_criterion === true) {
return callback_one(val); // where val is potentially some value you want to return (or a json object with results)
}
// more code you may have
}
Then in the original code that you posted, make this call instead (in the inner-most part):
recurse(startingPlace, otherFunc, function (results) {
// results is now a variable with the data returned at the end of recursion
console.log ("Recursion finished with results " + results);
callback(); // the callback that you wanted to call right from the beginning
});
Just spend some time and try to understand my explanation. When you understand, then you will know node. This is the node philosophy in one post. I hope it is clear. Your very first example should look like this:
var start = function(callback) {
request.get({
url: 'aaa.com'
}, function (error, response, body) {
var startingPlace = JSON.parse(body).id;
recurse(startingPlace, otherFunc, function (results) {
console.log ("Recursion finished with results " + results);
callback();
});
});
};
Below is only additional information in case you are interested. Otherwise you are set with the above.
Typically in node.js though, people return an error value as well, so that the caller knows if the function that was called has finished successfully. There is no big mystery here. Instead of returning just results people make a call of the form
return callback_one(null, val);
Then in the other function you can have:
recurse(startingPlace, otherFunc, function (recError, results) {
if (recErr) {
// treat the error from recursion
return callback(); // important: use return, otherwise you will keep on executing whatever is there after the if part when the callback ends ;)
}
// No problems/errors
console.log ("Recursion finished with results " + results);
callback(); // writing down `return callback();` is not a bad habit when you want to stop execution there and actually call the callback()
});
Update with my suggestion
This is my suggestion for the recursive function, but before that, it looks like you need to define your own get:
function myGet (a, callback) {
request.get(a, function (error, response, body) {
var nextPlace = JSON.parse(body).place;
return callback(null, nextPlace); // null for no errors, and return the nextPlace to async
});
}
var recurse = function(startingPlace, callback2) {
request.get({
url: 'bbb'
}, function(error1, response1, body1) {
// store body somewhere outside these funtions
// make second request
request.get({
url: 'ccc'
}, function(error2, response2, body2) {
var anArray = JSON.parse(body2).stuff;
if (anArray) {
// The function that you want to call for each element of the array is `get`.
// So, prepare these calls, but you also need to pass different arguments
// and this is where `bind` comes into the picture and the link that I gave earlier.
var theParallelCalls = [];
for (var i = 0; i < anArray.length; i++) {
theParallelCalls.push(myGet.bind(null, {url: 'ddd'})); // Here, during the execution, parallel will pass its own callback as third argument of `myGet`; this is why we have callback and callback2 in the code
}
// Now perform the parallel calls:
async.parallel(theParallelCalls, function (error3, results) {
// All the parallel calls have returned
for (var i = 0; i < results.length; i++) {
var nextPlace = results[i];
recurse(nextPlace, callback2);
}
});
} else {
return callback2(null);
}
});
});
};
Note that I assume that the get request for 'bbb' is always followed by a get request for 'ccc'. In other words, you have not hidden a return point for the recursive calls where you have the comments.
Typically when you write a recursive function it will do something and then either call itself or return.
You need to define callback in the scope of the recursive function (i.e. recurse instead of start), and you need to call it at the point where you would normally return.
So, a hypothetical example would look something like:
get_all_pages(callback, page) {
page = page || 1;
request.get({
url: "http://example.com/getPage.php",
data: { page_number: 1 },
success: function (data) {
if (data.is_last_page) {
// We are at the end so we call the callback
callback(page);
} else {
// We are not at the end so we recurse
get_all_pages(callback, page + 1);
}
}
}
}
function show_page_count(data) {
alert(data);
}
get_all_pages(show_page_count);
I think you might find caolan/async useful. Look especially into async.waterfall. It will allow you to pass results from a callback from another and when done, do something with the results.
Example:
async.waterfall([
function(cb) {
request.get({
url: 'aaa.com'
}, function(err, res, body) {
if(err) {
return cb(err);
}
cb(null, JSON.parse(body).id);
});
},
function(id, cb) {
// do that otherFunc now
// ...
cb(); // remember to pass result here
}
], function (err, result) {
// do something with possible error and result now
});
If your recursive function is synchronous, just call the callback on the next line:
var start = function(callback) {
request.get({
url: 'aaa.com'
}, function (error, response, body) {
var startingPlace = JSON.parse(body).id;
recurse(startingPlace, otherFunc);
// Call output function AFTER recursion has completed
callback();
});
};
Else you need to keep a reference to the callback in your recursive function.
Pass the callback as an argument to the function and call it whenever it is finished.
var start = function(callback) {
request.get({
url: 'aaa.com'
}, function (error, response, body) {
var startingPlace = JSON.parse(body).id;
recurse(startingPlace, otherFunc, callback);
});
};
Build your code from this example:
var udpate = function (callback){
//Do stuff
callback(null);
}
function doUpdate() {
update(updateDone)
}
function updateDone(err) {
if (err)
throw err;
else
doUpdate()
}
doUpdate();
With ES6, 'es6-deferred' & 'q'. You could try as following,
var Q = require('q');
var Deferred = require('es6-deferred');
const process = (id) => {
var request = new Deferred();
const ids =//do something and get the data;
const subPromises = ids.map(id => process(id));
Q.all(subPromises).then(function () {
request.resolve();
})
.catch(error => {
console.log(error);
});
return request.promise
}
process("testId").then(() => {
console.log("done");
});
I want to call a function after an asynchronous for loop iterating through values of an Javascript object finishes executing. I have the following code
for (course in courses) {
var url = '...' + courses[course];
request(url, (function (course) {
return function (err, resp, body) {
$ = cheerio.load(body);
//Some code for which I use object values
};
})(course));
}
This can be done in vanilla JS, but I recommend the async module, which is the most popular library for handling async code in Node.js. For example, with async.each:
var async = require('async');
var courseIds = Object.keys(courses);
// Function for handling each course.
function perCourse(courseId, callback) {
var course = courses[courseId];
// do something with each course.
callback();
}
async.each(courseIds, perCourse, function (err) {
// Executed after each course has been processed.
});
If you want to use a result from each iteration, then async.map is similar, but passes an array of results to the second argument of the callback.
If you prefer vanilla JS, then this will work in place of async.each:
function each(list, func, callback) {
// Avoid emptying the original list.
var listCopy = list.slice(0);
// Consumes the list an element at a time from the left.
// If you are concerned with overhead in using the shift
// you can accomplish the same with an iterator.
function doOne(err) {
if (err) {
return callback(err);
}
if (listCopy.length === 0) {
return callback();
}
var thisElem = listCopy.shift();
func(thisElem, doOne);
}
doOne();
}
(taken from a gist I wrote a while back)
I strongly suggest that you use the async library however. Async is fiddly to write, and functions like async.auto are brilliant.
A possible simple JS solution would be to do something like this.
var courses = {
lorum: 'fee',
ipsum: 'fy',
selum: 'foe'
};
var keys = Object.keys(courses);
var waiting = keys.length;
function completedAll() {
console.log('completed all');
}
function callOnCourseComplete(course, func) {
console.log('completed', course);
waiting -= 1;
if (!waiting) {
func();
}
}
var delay = 10000;
keys.forEach(function(course) {
var url = '...' + courses[course];
console.log('request', url);
setTimeout((function(closureCourse) {
return function( /* err, resp, body */ ) {
// Some code for which I use object values
callOnCourseComplete(closureCourse, completedAll);
};
}(course)), (delay /= 2));
});
Update: Probably a better Javascript solution would be to use Promises
const courses = {
lorum: 'fee',
ipsum: 'fy',
selum: 'foe',
};
function completedAll() {
console.log('completed all');
}
function callOnCourseComplete(courseName) {
console.log('completed', courseName);
}
let delay = 10000;
const arrayOfPromises = Object.keys(courses).map(courseName => (
new Promise((resolve, reject) => {
const url = `...${courses[courseName]}`;
console.log('request', url);
setTimeout((err, resp, body) => {
if (err) {
reject(err);
}
// Some code for which I use object values
resolve(courseName);
}, (delay /= 2));
}))
.then(callOnCourseComplete));
Promise.all(arrayOfPromises)
.then(completedAll)
.catch(console.error);
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.