I don't understand how to call a function recursively in node.js for example:
var releaseStock = function (callback) {
getItems(function (err, items) {
if (err) {
return callback(err);
} else {
if (items) {
return callback(items);
} else {
setTimeout(function() {
releaseStock(callback);
}, 5000);
}
}
});
};
How can i make it work?
I'm not entirely sure what you want to do, but I suspect it is something along the lines of:
var releaseStock = function(callback) {
// get items from somewhere:
var items = getItems();
if (!items) {
// if there are no items, try again (recurse!):
return releaseStock(callback);
}
// if there are items, give them to the callback function:
return callback(items);
};
Related
I have a question about asynchronous function. Here my function "My_function":
function My_function (my_name, callback){
stmt = db.prepare ("SELECT number_table1 from my_table1 WHERE user=?");
stmt.bind(my_name);
stmt.get(function(error,row){
if(error){
throw err;
}
else{
if(row){
callback(number_table1);
}
else{
console.log("error");
}
}
});
}
Work fine but I have 2 tables and I need do other query and I need add two numbers so... in my function I need do too this query:
stmt = db.prepare ("SELECT number_table2 from my_table2 WHERE user=?");
and finally return back in my callback "number_table1 + number_table2".
Somebody know how to solve it? Thanks in advance.
Best regards!
In cases like your's I like to use the async module, because the code will be more legible. Example:
var async = require('async');
function My_function(my_name, callback) {
var stmt = db.prepare("SELECT number_table1 from my_table1 WHERE user=?");
stmt.bind(my_name);
stmt.get(function (error, row) {
if (error) {
callback(error, null);
} else {
if (row) {
callback(null, number_table1);
} else {
callback(new Error("row not found"), null);
}
}
});
}
//if you need the results in a specific order, you can use "series" instead of "parallel".
async.parallel(
//array of functions
[
function (callback) {
My_function('firstName', callback);
},
function (callback) {
My_function('secondName', callback);
}
],
//results when all functions ends
//the results array will equal [number_table1, number_table2], but the order can be different, because of the parallelism
function (err, results) {
if (err) {
//handle the err
} else {
//do something
}
}
);
Docs:
http://caolan.github.io/async/docs.html#parallel or
http://caolan.github.io/async/docs.html#series
You need to synchronize the functions so that you can be sure both their results are ready before calling back. You can do this using promises: https://www.promisejs.org/
Make two regular functions (no callbacks), one for each query (function1, function2)
Make both return a promise
Then you can do
function My_function(my_name) {
var value1;
function1(my_name)
.then(function(resultFromFunction1) {
value1 = resultFromFunction1;
return function2(my_name);
})
.then(function(resultFromFunction2) {
var result = value1 + resultFromFunction2;
return result;
});
}
}
Make sure to catch errors and handle different outcomes, what I presented is its simplest form.
Update
Here is an example of a function doing a query and returning a promise
function1 = function(user) {
return new Promise(function (resolve, reject) {
pool.getConnection(function (err, connection) {
if(err) {
reject ({status : false, message : "Error in connection database"});
} else {
connection.query('SELECT number_table1 from my_table1 WHERE user=?', [user], function(err, rows){
connection.release();
if(!err) {
resolve ({status: true, message: rows});
} else {
reject ({status: false, message: err});
}
});
}
});
});
}
Make the table names function parameters. Convert that function to use async/await or promise. Use Promise.all to run both queries.
I am running a cron job with node with mongodb as the database. I am trying to close db connection and exit the process once the curr_1 each loop has executed completely.
However the exit() is called while function_2 is being executed. I understand this is due to the callback and is async in nature.
How do I make sure exit is called only once the curr_1.each is complete?
Any solution without promises?
function function_1(obj){
var curr_1 = coll_1.find({})
curr_1.each(function(err, doc) {
function_2(doc)
});
exit(obj)
}
function function_2(obj) {
coll_2.findOne({}, function(err, document) {
dosomeprocess(obj)
})
}
function exit(obj) {
// Close connection
console.log('closing connection')
obj.db.close();
process.exit();
}
It's a job for Node async....
For example:
async.each(
curr_1, // the collection to iterate over
function(doc, callback) { // the function, which is passed each
// document of the collection, and a
// callback to call when doc handling
// is complete (or an error occurs)
function_2(doc);
},
function(err) { // callback called when all iteratee functions
// have finished, or an error occurs
if (err) {
// handle errors...
}
exit(obj); // called when all documents have been processed
}
);
Without using any library:
function function_1(obj, callback) {
var curr_1 = coll_1.find({})
curr_1.each(function(err, doc) {
callback(err, doc);
});
}
function function_2(err, obj) {
coll_2.findOne({}, function(err, document) {
dosomeprocess(obj)
exit(err, obj);
})
}
function exit(err, obj) {
// Close connection
console.log('closing connection')
obj.db.close();
process.exit();
}
function_1(obj, function_2);
Using async module
var async = require('async');
async.waterfall([
function function_1(callback) {
var curr_1 = coll_1.find({})
curr_1.each(function(err, doc) {
if (err) {
callback(err, null)
} else {
allback(null, doc)
}
});
},
function function_2(obj, callback) {
coll_2.findOne({}, function(err, document) {
if (err) {
callback(err, null);
} else {
dosomeprocess(obj)
callback(null, obj);
}
})
}
], function done() {
obj.db.close();
process.exit();
});
Simply give a condition in your loop using counter.
function function_1(obj){
var curr_1 = coll_1.find({})
var curr_1Length = curr_1.length;
var counter = 0;
curr_1.each(function(err, doc) {
++counter;
//Check condition everytime for the last occurance of loop
if(counter == curr_1Length - 1){
exit(obj)
}
function_2(doc)
});
}
Hope it helps :)
the following function creates new folder on my server via xmlrpc
var createFolder = function(folder_name) {
var defer = Q.defer();
client.methodCall('create_folder', [sessionID, folder_name], function(err, resp) {
if (err) {
if (err.responseString && err.responseString.match('already exist')) {
//call the same function recursively with folder_name+Math.round(Math.random()*100)
} else {
defer.reject(err);
}
} else {
defer.resolve(folder_name);
}
});
return defer.promise;
}
The functions creates a new folder successfully
However, if folder already exists i want to fire this function again recursively with new folder name and then return it in promise so that whenever this function is called it'll return the folder name doesn't matter how many times it was executed
something like
createFolder('directory').then(function(resp){
console.log(resp);// may return directory || directory1 .... etc
});
**EDIT **
so i manged to achieve this by passing the defer object
let me know if there are more elegant ways of achieving this
var createFolder = function(folder_name,defer) {
defer =defer || Q.defer();
client.methodCall('create_folder', [sessionID, folder_name], function(err, resp) {
if (err) {
if (err.responseString && err.responseString.match('already exist')) {
return createFolder(folder_name+Math.round(Math.random()*100,defer)
} else {
defer.reject(err);
}
} else {
defer.resolve(folder_name);
}
});
return defer.promise;
}
Never do any logic in plain (non-promise) callbacks. Promisify at the lowest level:
var defer = Q.defer();
client.methodCall('create_folder', [sessionID, folder_name], function(err, resp) {
if (err) defer.reject(err);
else defer.resolve(folder_name);
});
return defer.promise;
Or much simpler with Q.ninvoke:
return Q.ninvoke(client, 'methodCall', 'create_folder', [sessionID, folder_name]);
Now we can start implementing our recursion. It's quite simple with a then callback, from which you can return another promise. In your case:
function createFolder(folder_name) {
return Q.ninvoke(client, 'methodCall', 'create_folder', [sessionID, folder_name])
.catch(function(err) {
if (err.responseString && err.responseString.match('already exist')) {
return createFolder(folder_name+Math.floor(Math.random()*100));
} else {
throw err;
}
});
}
Here is a bad simple way of solving your problem:
var createFolder = function(folder_name) {
var defer = Q.defer();
client.methodCall('create_folder', [sessionID, folder_name], function(err, resp) {
if (err) {
if (err.responseString && err.responseString.match('already exist')) {
//call the same function recursively with folder_name+Math.round(Math.random()*100)
defer.resolve(createFolder(folder_name+Math.round(Math.random()*100)));
} else {
defer.reject(err);
}
} else {
defer.resolve(folder_name);
}
});
return defer.promise;
}
However, defer is considered bad practice. Here is a very nice article about promises.
You should favor something like:
var createFolder = function(folder_name) {
return Q.Promise(function(resolve, reject){
client.methodCall('create_folder', [sessionID, folder_name], function(err, resp) {
if (err) {
if (err.responseString && err.responseString.match('already exist')) {
//call the same function recursively with folder_name+Math.round(Math.random()*100)
resolve(createFolder(folder_name+Math.round(Math.random()*100)));
} else {
reject(err);
}
} else {
resolve(folder_name);
}
});
});
}
EDIT: as noted by #Bergi, this is still not right and hard to debug. Any potential errors thrown from the callback of methodCall won't actually reject the promise and will most likely be swallowed (even though this callback seems very little error-prone, it might evolve). Please refer to his answer for a better way of doing this.
Also, see the official Q doc here.
I've read through a lot of different articles on promisejs but can't seem to get it to work for my code. I have async code that works and does what I need but it's very long and doesn't look as clean as it could with promise.
Here's the two links I've been really looking into: http://jabberwocky.eu/2013/02/15/promises-in-javascript-with-q/ and https://spring.io/understanding/javascript-promises.
mainCode.js
accountModel.findOne({username: body.username}, function(err, usernameFound) {
console.log("here");
if (err) {
console.log(err);
} else {
console.log("here1");
anotherClass.duplicateUsername(usernameFound, function(err, noerr) {
if (err) {
console.log("error");
res.status(409).send("username");
} else {
console.log("here2");
accountModel.findOne({email: body.email}, function(err, emailFound) {
if (err) {
console.log("error2");
} else {
console.log("here3");
console.log(emailFound);
}
});
}
});
}
});
// anotherclass.duplicateUsername
anotherClass.prototype.duplicateUsername = function(usernameFound, callback) {
if (usernameFound) {
callback(usernameFound);
} else {
callback();
}
}
current promise code (in mainCode.js):
var promise = userModel.findOne({
username: body.username
}).exec();
promise.then(function(usernameFound) {
console.log("usernameFound")
return userCheck.duplicateUsername(usernameFound);
}).then(function(usernameFound) {
console.log("NOERR:" + usernameFound + ":NOERR");
console.log("noerror");
return;
}, function(error) {
console.log(err);
console.log("error");
res.sendStatus(409);
return;
});
When I run my promise code, it goes to duplicateUsername, does callback() but then doesn't print anything in the promise code.
duplicationUsername needs to return a promise, otherwise the promise chaining will get the value returned from calling callback (which would be undefined).
Something like this should work:
anotherClass.prototype.duplicateUsername = function(usernameFound) {
var deferred = Q.defer();
if (usernameFound) {
deferred.resolve(usernameFound);
} else {
deferred.reject();
}
return deferred.promise;
}
So it seems like I needed to "promisify" my own functions before I could use them.
Here's how I did it with Q:
var Q = require('q');
anotherClass.prototype.duplicateUsername = function(username, callback) {
return new Promise(function(resolve, reject) {
var deferred = Q.defer();
if (usernameFound) {
deferred.reject("error);
} else {
deferred.resolve("no err: duplicate username");
}
deferred.promise.nodeify(callback);
return deferred.promise;
});
}
Here's how to do it with Bluebird:
userCheck.prototype.duplicateUsername = function(usernameFound) {
return new Promise(function(resolve, reject) {
if (usernameFound) {
reject("error");
} else {
resolve();
}
});
}
Then in my mainClass, I just used them by calling the methods and then .then(//blah)
I'm getting a 'callback already used' error and I don't know why. I am using async and want to chain two functions because the second function depends on the first function to complete.
I'm new-ish to Node.js and still wrapping my head around async/callbacks. Thanks so much for helping out.
getCdn takes in cnames, and if the cname is part of a CDN it pushes the results into a global variable called cdnAttrs.
function getCdn(cnameDict, callback) {
// cdnAttributes contains associative array with each web attribute: {name_in_db : code_snippet_to_find_in_cname}
for (var key in cdnAttributes) {
if (cdnAttributes.hasOwnProperty(key)) {
var snippet = -1;
// some technologies contain multiple code snippets, in that case they are stored as array. Single code snippets are stored as string
if (!Array.isArray(cdnAttributes[key])) {
snippet = cnameDict['cname'].indexOf(cdnAttributes[key])
}
else {
// check each code snippet within the array, if any match the source code, update 'snippet'
for (var n = 0; n < cdnAttributes[key].length; n++) {
var val = cnameDict['cname'].indexOf(cdnAttributes[key][n])
if (val > -1) {
snippet = val
}
}
}
// if attribute found in tag, create cdnAttrs[cdn] = [{from: hostname, proof: cname}, {from: hostname2, proof: cname2}, ...]
if (snippet > -1) {
try {
cdnAttrs[key].push(cnameDict);
}
catch (e) {
cdnAttrs[key] = [];
cdnAttrs[key].push(cnameDict);
}
callback();
} else {
callback();
}
} else {
callback();
}
}
}
My async function looks like this:
async.series([
// THIS FUNCTION WORKS FINE...
function(callback) {
async.each(toCheck, function(hostname, callback) {
getCname(hostname, callback);
},callback);
},
// THIS FUNCTION RETURNS RETURNS Error("Callback was already called.")
function(callback) {
async.each(toCheckCnames, function(cnameDict, callback) {
getCdn(cnameDict, callback);
},callback);
}
], function(err){
if(err) {
console.log('ERROR');
}else{
console.log('toCheckCnames is done: '+JSON.stringify(toCheckCnames));
console.log('cdnAttrs is done: '+JSON.stringify(cdnAttrs));
}
})
the getCnames function works:
function getCname(hostname, callback){
dns.resolve(hostname, 'CNAME', function(error, cname) {
if (cname) {
toCheckCnames.push({from: hostname, cname: cname[0]});
callback();
}
// if not CNAMEd, check SOA on www.domain.com and domain.com
else {
if (hostname.slice(0,4) == 'www.') {
hostname = hostname.slice(4);
}
nativedns.resolve(hostname, 'SOA', function(error, records) {
if(!error && records) {
toCheckCnames.push({from: hostname, cname: records[0]['primary']});
callback();
}
else if (!error) {
hostname = 'www.'+ hostname
nativedns.resolve(hostname, 'SOA', function(error, records) {
if (!error) {
toCheckCnames.push({from: hostname, cname: records[0]['primary']});
callback();
}
else callback()
});
}
else callback()
});
}
});
}
Your getCdn function is a loop that will call the callback after each iteration. If calling the callback is intended to stop the loop execution, you can do return callback(). Otherwise you need to reorganize your code to only call the callback once when the function is done.
UPDATE:
You can also simplify your async.each calls:
// Was this
async.each(toCheck, function(hostname, callback) {
getCname(hostname, callback);
},callback);
// Could be this
async.each(toCheck, getCname, callback);