It's possible I don't understand Node's event loop well enough.
Say I have a function foo which contains an asynchronous function async_func. Do I have
//1
function foo(callback) {
//stuff here
async_func(function() {
//do something
callback();
});
//this eventually get executed
}
or
//2
function foo(callback) {
//stuff here
async_func(function() {
//do something
return callback();
});
//never executed
}
Actually, in your sample 2, //never executed will be execute every time. It's returning from the callback, not from the wrapping function.
Sometimes the caller actually expects some return value and the behavior can change based on that. Another common reason to see a return callback() is just a clear way of short circuiting the function you're in. For example.
function doSomething(callback) {
something(function(err, data) {
if(err) return callback(err);
// Only run if no error
});
// Always run
}
Even though the return value isn't being used, it's using return to ensure that execution doesn't continue past the error conditional. You could just as easily write it this way which has the same effect.
function doSomething(callback) {
something(function(err, data) {
if(err) {
callback(err);
return;
}
// Only run if no error
});
// Always run
}
Related
I'm working with callbacks to improve my program's efficiency. I would like to wait for my 'a' variable to get the value from callback if it hasn't already at some point. My code looks like this:
function myFunction(callback){
request("url", function(error, response, body) {
if (!error && response.statusCode == 200)
{
result = JSON.stringify(JSON.parse(body));
return callback(null, result);
}
else
{
return callback(error, null);
}
});
}
var a = myFunction(function(err, data){
if(!err)
{
return(data);
}
else
{
return(err);
}
});
//A CHUNK OF CODE EXECUTES HERE
//I'D LIKE TO CHECK IF THE a VARIABLE GOT THE VALUE (if the callback was executed), note that I don't want to nest the chunk inside the callback, but rather I'd like to check that outside function callback via IF statement/loop or some similar alternative
//MORE CODE CONNECTED TO THE CALLBACK
Any ideas on how I can wait for the variable to get the value if it didn't get it already by the time that the chunk of code is executed? I know that the sole intention of using callbacks is to not wait but in this instance it is necessary for me so just don't bother being annoying please :)
I'd suggest trying async/await and the request-promise-native module, this keeps the syntax very simple. If the call fails for some reason an error will be thrown, this is easy to catch:
For example:
const rp = require('request-promise-native');
async function testAsyncRequest() {
try {
let promise = rp('https://my-json-server.typicode.com/typicode/demo/posts');
// Do some stuff here...
let result = await promise;
console.log("Result: ", result);
} catch (err) {
console.error(`Something went wrong: ${err.message}`);
}
}
testAsyncRequest();
So I've ended up making a little compromise (had to nest), but the code works as intended:
var something;
function myFunction(callback){
request("url", function(error, response, body) {
if (!error && response.statusCode == 200)
{
result = JSON.stringify(JSON.parse(body));
return callback(null, result);
}
else
{
return callback(error, null);
}
});
}
var a = myFunction(function(err, data){
if(!err)
{
something = data;
return(data);
}
else
{
return(err);
}
});
//A CHUNK OF CODE EXECUTES HERE
var something_cachedValue=something;
doStuff();
function doStuff() {
if(something===something_cachedValue) {
setTimeout(doStuff, 10);
return;
}
something_cachedValue = something;
//MORE CODE CONNECTED TO THE CALLBACK
}
So basically I'm checking if the callback has completed every 10ms by re-calling the function. When it's completed, the "//MORE CODE CONNECTED TO THE CALLBACK" executes. Not the cleanest and most fancy way to do it but it does the job.
I tried aysnc.eachOfSeries but the code does not loop. It stops executing in the first iteration itself and freezes. I guess I made some error in returning the callback.
I also tried putting a callback inside the else block but gives callback already called error.
This async.eachOfSeries is nested inside one more eachOfSeries.
async.eachOfSeries(data, function (value2, val, callback) {
let jsonObj = data[val];
let email = jsonObj.ToEmail;
jsonObj['retailer'] = res[camp].retailer;
jsonObj['summary'] = 'f';
let tempObj = {};
tempObj[id] = jsonObj;
let options = { new: true};
let campId = id;
User.addCampaignResponse(email, campId, tempObj, options, function (err, results) {
if (err) {
throw err;
} else {
console.log("gets printed once");
Campaign.updateResponse(_id, function (err, results2) {
if (err)
throw err;
else {
console.log("Gets printed once");
callback(); //tried this but gives callback already called error
}
}) // console.log(results);
}
})
}, function (err) {
console.log("Doesn't print");
callback();
})
You must use it like this:
//async.eachOfSeries(data, function (value2, val, callback) { => wrong
async.eachOfSeries(data, function (value2, callback) {
//
})
Thought I'd leave this here in case someone else hits this thread.
The problem is that on your optional callback at the end:
}, function (err) {
console.log("Doesn't print");
callback();
});
You're calling the iterator callback and not an outer callback.
In the context of your code you probably have your async inside other function which callback is the one you should call on your async's optional callback at the end. Or just don't use the optional function, but never let it go through your iterator callback after ending the loop.
You should also test for error, something like:
}, function(err){
if(err){
debug('Something went wrong in async: ' +err);
outerCallback(err, somethingIfitsthecase);
}else{
outerCallback(null, somethingIfitsthecase);
}
});
Check caolan's documentation as Parth Acharya recommended.
Thanks
Best regards
Pedro Velez Silva
It is supposed to work that way.
if an error occurs, which is happening in your case. It runs the final callback function that you've provided.
Link :- https://caolan.github.io/async/docs.html#eachOfSeries
A callback which is called when all iteratee functions have finished,
or an error occurs. Invoked with (err).
I had a similar dilemma and I solved it by adding a callback in the async param and calling it at the end of the method as shown below.
async.eachOfSeries(data, async (item, index, callback: any) => {
// method body
callback();
})
Hi I am trying to execute a callback in a if statement but I get "TypeError: callback is not a function"
This is my code:
socket.on('authenticate', function (data, callback) {
// this works
callback("false");
// this doesn't work
if (data == "abc") {
callback("true");
}
});
Always check if callable function is passed, at least just do if(callback)
in Your problem there may be situation that clientside not waiting for callback (done emit call without passing callback argument).
Try this solution:
socket.on('authenticate', function (data, callback) {
console.debug('socket authenticate:', data); // for debug purposes
if (data == "abc") {
if(callback) { // callback may not be passed
callback(null, true); // keep in mind in callbacks first argument is error second is result
}
return; // return will prevent execution to go down, because most of times used for resulting call.
}
if(callback) { // callback may not be passed
callback("Not authenticated", false);
}
});
Where are people getting cb() from, is this a Node thing or vanilla JS thing?
For example:
Managing Node.js Callback Hell with Promises, Generators and Other Approaches
they're using cb() to I guess callback and return an error or a value or both in some cases depending on what the callback function sig is?
cb in the context you're describing it is how a vanilla callback function is passed into a (typically) asynchronous function, which is a common pattern in node.js (it's sometimes labelled next, but you can call it bananas if you so desire - it's just an argument).
Typically the first argument is an error object (often false - if all went as planned) and subsequent arguments are data of some form.
For example:
function myAsyncFunction(arg1, arg2, cb) {
// async things
cb(false, { data: 123 });
}
then using this function:
myAsyncFunction(10, 99, function onComplete(error, data) {
if (!error) {
// hooray, everything went as planned
} else {
// disaster - retry / respond with an error etc
}
});
Promises are an alternative to this design pattern where you would return a Promise object from myAsyncFunction
For example:
function myAsyncFunction2(arg1, arg2) {
return new Promise(function resolution(resolve, reject, {
// async things
resolve({ data: 123 });
});
}
then using this function:
myAsyncFunction2(10, 99)
.then(function onSuccess(data) {
// success - send a 200 code etc
})
.catch(function onError(error) {
// oh noes - 500
});
They're basically the same thing, just written slightly differently. Promises aren't supported especially widely in a native form, but if put through a transpiler (I'd recommend babel) during a build step they should perform reliably enough in a browser too.
Callbacks will always work in a browser with no shimming / transpilation.
node.js has lots of asynchronous operations that take a completion callback as an argument. This is very common in various node.js APIs.
The node.js convention for this callback is that the first argument passed to the callback is an error code. A falsey value for this first argument means that there is no error.
For example:
fs.readFile("test.txt", function(err, data) {
if (!err) {
console.log("file data is: " + data);
}
});
A function you create yourself may also define it's own callback in order to communicate the end of one or more asynchronous operations.
function getData(id, cb) {
var fname = "datafile-" + id + ".txt";
fs.readFile(fname, function(err, data) {
if (err) {
cb(err);
} else if (data.slice(0, 6) !== "Header"){
// proper header not found at beginning of file data
cb(new Error("Invalid header"));
} else {
cb(0, data);
}
});
}
// usage:
getData(29, function(err, data) {
if (!err) {
console.log(data);
}
});
From the Vanilla JS, you can declare a function and pass throuw parameters a declaration of another function, that can called async
https://developer.mozilla.org/en-US/docs/Glossary/Callback_function
I'm just doing my first bit of Node async stuff, I wanted to do two queries to a DB and then print the results one after the other, my code is as follows:
console.log(req.query);
function logAllThings(err,things){
if (err){
console.log(err);
} else {
console.log(things);
};
};
async.parallel([
function(callback) { //This is the first task, and callback is its callback task
Event.find({}, function(err, events) {
logAllThings(err,events);
});
},
function(callback) { //This is the second task, and callback is its callback task
Organisation.find({}, function(err,organisations) {
logAllThings(err,organisations);
}); //Since we don't do anything interesting in db.save()'s callback, we might as well just pass in the task callback
}
], function(err) { //This is the final callback
console.log('Both should now be printed out');
});
The issue I have is that the second function (the one that returns organisations) prints them fine. However the one that is meant to return events simply does not and returns {} despite the fact I know the query works as I've tested it elsewhere.
Any help would be appreciated
Thanks
You have to call the callback function passed to each of your waterfall functions, otherwise it won't know when it's finished. Try this:
async.parallel([
function(callback) { //This is the first task, and callback is its callback task
Event.find({}, function(err, events) {
if (err) return callback(err);
logAllThings(err,events);
callback();
});
},
function(callback) { //This is the second task, and callback is its callback task
Organisation.find({}, function(err,organisations) {
if (err) return callback(err);
logAllThings(err,organisations);
callback();
}); //Since we don't do anything interesting in db.save()'s callback, we might as well just pass in the task callback
}
], function(err) { //This is the final callback
console.log('Both should now be printed out');
});