I went through many examples of nodejs with callbacks, but didn't understand how exactly they work. I know that they're executed after the function, of which they're a part of is done, but I didn't understand callbacks as a function. Let me give an example:
function processData (callback) {
fetchData(function (err, data) {
if (err) {
console.log("An error has occured. Abort everything!");
callback(err);
}
data += 1;
callback(data);
});
}
Here, what how do callback(err) and callback(data) execute, as in, is it an alias for some other function to which we're passing the parameters - err/ data? If so, which function does it call when we write callback(parameter)?
Here's another example :
var express = require("express");
var bodyParser = require("body-parser");
var multer = require('multer');
var app = express();
app.use(bodyParser.json());
var storage = multer.diskStorage({
destination: function (req, file, callback) {
callback(null, './uploads');
},
filename: function (req, file, callback) {
callback(null, file.fieldname + '-' + Date.now());
}
});
var upload = multer({ storage : storage }).array('userPhoto',2);
app.get('/',function(req,res){
res.sendFile(__dirname + "/index.html");
});
app.post('/api/photo',function(req,res){
upload(req,res,function(err) {
//console.log(req.body);
//console.log(req.files);
if(err) {
return res.end("Error uploading file.");
}
res.end("File is uploaded");
});
});
app.listen(3000,function(){
console.log("Working on port 3000");
});
Again I sort of understood the other type of callback - suppose, here fs.readdir
fs.readdir(path, function (err, files) {
if (err) console.error(err);
for (var i = 0; i<files.length; i++) console.log(files[i];
}
console.log("done");
I know how this executes, most probably it'll print done first, and then prints the list of files, as soon as readdir is executed with files having the list of files.
But I didn't exactly understand the first and second code snippet. Can someone please explain that in simple terms, specific to the multer code snippet?
Callbacks and asynchronous behaviour are two different but related things. They're relate in the sense that asynchronous code can use callback to execute code in the intended sequence. But callbacks by themselves are just the result of the language treating functions as objects.
In your examples, you can see two sides of the callback story. One is the implementation and the other is the usage. The first code is an example of how to implement a function that accepts a callback. The last is an example of how to use a function that accepts a callback.
For simplicity let's ignore asynchronous behaviour for now and implement synchronous callbacks.
The following is an example of a function that loops through an array to construct a string:
function joiner (arr, callback) {
var return_value = "";
for (var i=0; i<arr.length; i++) {
return_value += callback(arr[i]); // use callback on each item
}
return return_value;
}
Once we've implemented the function above we can use it:
var result = joiner(['this','is','cool'],function(x) {
return "--> " + x + "!";
});
So, how do we know what arguments the callback accept? It accepts what the caller passes to it.
How do we know what arguments to pass to a callback function? We pass what the function is defined to accept?
If that sounds circular, it is. So how we really know what arguments to pass to a callback is:
If it's an API we created, we can do anything we want
If it's an API someone else created, read the documentation (or if your IDE can do it, check out the intellisense)
Callbacks gone meta
The above explanation is of course only the simplest example of callbacks: functions that accept a callback. The express example (and indeed your first example) shows how you can get even more complicated. You can write functions that accept a callback that accept a callback.
A simplified example of functions that accept a callback that accept a callback is something like this:
function processData (data) {
return "<" + data + ">";
}
function render (callback) {
return callback(processData);
}
So the render function above accepts a callback that will be passed a callback that returns a string. So if we now do:
var result = render(function(callback){
return "--" + callback('hello');
});
we will get:
--<hello>
As you can see, the callback we passed to render does not know how to process the data but render does (it calls processData). And render does not know what the data is but the callback we passed to it does ('hello'). Being able to pass a callback to a callback allows two pieces of code to cooperate without exposing each other's implementation details.
Async code uses this technique to order the sequence of code being executed. Because async code cannot return a value you can only pass it a callback so that once it gets its value your code (callback) can continue.
If you think about it, the Express API could have easily been implemented so that request handler callbacks must return the HTML page instead of calling res.send(). Indeed, this is how frameworks work in many languages. But that means that the request handler cannot execute asynchronous code to fetch data. Because Express doesn't want to limit you to using only synchronous code it passes an object with callback methods instead (res). This "design pattern" is called a monad. The one you see in Express is a continuation monad while the one you see in fs.readdir() is an I/O monad.
First, you got confused because you're not following the Node JS standards for passing parameters to asynchronous function. Every async callback must take 2 parameters. First should always be error and 2nd should always be data. Refer to how the callbacks for readdir and fetchData are defined.
So your code should ideally look like
function processData (callback) {
fetchData(function (err, data) {
if (err) {
console.log("An error has occured. Abort everything!");
callback(err, null); // here data will be null
}
data += 1;
callback(null, data); // here err will be null.
});
}
And you will define your callback function for processData at the time you call it like
processData(function(err, data){
err ? console.error(err) : console.log(data);
});
This way, in your callback, you know for sure whether the function has succeeded or failed.
Related
I have this function which gets some data from my database but i'm having a trouble calling the function and getting the proper response
function getEvents()
{
var x = [];
var l = dbCollection['e'].find({}).forEach(function(y) {
x.push(y);
});
return x;
});
and another function which calls this function but it always returns undefined.
How can i make the function wait till mongoose has finished filling up the array?
Thanks for the help! My life
dbCollection['e'].find is called non-blocking way so you are returning x before filling. You need to use callbacks or some mongoose promises. You can get all returning values from database like following snippet
function getEvents(callback) {
dbCollection['e'].find({}, function(error, results) {
// results is array.
// if you need to filter results you can do it here
return callback(error, results);
})
}
Whenever you need to call getEvents function you need to pass a callback to it.
getEvents(function(error, results) {
console.log(results); // you have results here
})
You should read mongoose docs for how queries work.
There is also support for promises in mongoose. You can check this url for more information about promises.
The solution proposed by #orhankutlu should work fine.
I will give another solution using promise. You can choose one between these two solutions depending on your style of programming.
Solution using promise:
function getEvents() {
return new Promise(function(resolve, reject){
dbCollection['e'].find({}, function(error, results) {
if (error) return reject(error);
var x = [];
results.forEach(function(y){
x.push(y);
});
// forEach() is a blocking call,
// so the promise will be resolved only
// after the forEach completes
return resolve(x);
});
});
};
Calling getEvents():
getEvents().then(function(result){
console.log(result); //should print 'x'
}).catch(function(err){
// Handle error here in case the promise is rejected
});
I will encourage you to try both the approaches, ie, using callbacks and using promises. Hope you find it useful!
So I'm new to Node.js and Im just wondering if the way I have my code setup makes sense. Im coming from a Java background so the nested callback structure is new. I have a Node program that runs a bunch of code that I broke down into different methods. The thing is that the methods need to be called in order. My code has this structure right now:
functionOne(data, callback(err) {
functionTwo(data, callback(err) {
functionThree(data, callback(err) {
functionFour(data, callback(err) {
//Code
});
});
});
});
This is very minimalistic, but is this structure ok? With Java, I'd take the return values of all the methods, then just pass them to the next function. From my understanding so far, the Java approach I just mentioned is one of the main things that Node.js was trying to eliminate. But anyway... Does that structure look ok, and is that how its intended to look? Just want to be sure that I'm not making any major errors with Node in general. Thanks!
Your code structure looks fine if you work with callback pattern.
But if you're interested in make your code cleaner and readable you would like to use Promises in your asynchronous function, so instead of pass a callback to your functions you could do something like this :
function asyncFunction (data){
return new Promise(function(resolve, reject){
// Do something with data
// Here you can call reject(error) to throw an error
resolve();
});
}
And instead of nested function callbacks you can call then method of Promise.
asyncFunction(data)
.then(function(){
// Promise resolved
// Something has been done with data
});
With Promises you can also execute async fuctions in parallel :
Promise.all([asyncFunctionA(data), asyncFunctionB(data), asyncFunctionC(data)])
.then(function(){...});
EDIT
If you need to pass values of one function to another, your code should look like this :
asyncFunctionA(data)
.then(function(dataA){
return asyncFunctionB(dataA);
})
.then(function(dataB){
return asyncFunctionC(dataB);
})
.then(function(dataC){
// ...
});
You should try to use promises to avoid your callback hell, so it could be something like these...
const Q = require('q'); // you can do a research for this module.
var myModule = {};
myModule.functionOne = (params) => {
const deferred = Q.defer(); // wait for this to complete
// body function
deferred.resolve(data); // this would be the result of this function
return deferred.promise; // data is the output on your function
}
myModule.functionTwo = (params) => {
const deferred = Q.defer(); // wait for this to complete
// body function
deferred.resolve(data); // this would be the result of this function
return deferred.promise; // data is the output on your function
}
myModule.doAll = (params) => {
myModule.functionOne(params)
.then((outputFunctionOne) => {
// this is called after functionOne ends
return myModule.functionTwo(outputFunctionOne);
})
.then((outputFunctionTwo) => {
// this is called after function 2 ends
if (outputFunctionTwo.success) {
// if everything ok, resolve the promise with the final output
deferred.resolve(outputFunctionTwo);
} else {
// reject the promise with an error message
deferred.reject('error');
}
})
.fail((err) => {
// this is call if the promise is rejected or an exception is thrown
console.log(err); // TODO: Error handling
})
.done();
}
module.exports = myModule;
You can Chain as many promises as you want really easily, that way you get rid of the callback hell. Best part, you can do promises on Javascript or Node.js
Reference Link https://github.com/kriskowal/q
Hope this helps
Most of the other answers give Promise/A as the answer to your callback woes. This is correct, and will work for you. However I'd like to give you another option, if you are willing to drop javascript as your working language.
Introducing Iced Coffee, a branch of the CoffeeScript project.
With Iced Coffee you would write:
await functionOne data, defer err
await functionTwo data, defer err2
await functionThree data, defer err3
//etc
This then compiles to the CoffeeScript:
functionOne data, (err) ->
functionTwo data, (err2) ->
functionThree data, (err3) ->
//etc
Which then compiles to your Javascript.
functionOne(data, callback(err) {
functionTwo(data, callback(err2) {
functionThree(data, callback(err3) {
//etc
});
});
});
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
maybe i am not understanding promises, why am i getting redirected before functions.dioNic ends?
app.get('/dionic/scan', function(req, res) {
var functions = require("./functions.js");
var scrape = require("./scrapers/dionic.js");
//render index.ejs file
scrape.scrapeDio
// Remove duplicates in array data
.then(dedupe)
// Save
.then(functions.dioNic)
.then(console.log("Guardado"))
.then(res.redirect('/'));
});
You're calling these functions and passing their return values to then instead of passing the functions themselves:
.then(console.log("Guardado"))
.then(res.redirect('/'));
This is effectively the same problem as described here.
You can use bind to get a function with the argument bound:
.then(console.log.bind(console, "Guardado"))
.then(res.redirect.bind(res, '/'));
You could also use anonymous functions, if you don't like the look of bind:
.then( function () { console.log("Guardado") })
.then( function () { res.redirect('/') });
I am new to promises Q node module.
How to resolve values for promise object.
Here is my example:
function fs_readFile (file, encoding) {
var deferred = Q.defer()
fs.readFile(file, encoding, function (err, data) {
if (err)
deferred.reject(err)
else
deferred.resolve(data)
})
return deferred.promise
}
Now i am calling the function:
var promise=fs_readFile('myfile.txt').then(console.log, console.error);
Now i can get my file content data from then fulfill call back function but how can i get my file content data from returned promises object. like synchronously .
can any one help me!!
Promises help by encapsulating the result of an asynchronous operation but because the operation hasn't finished yet you can't actually get the result synchronously. You can get some of the appearance of synchronous behaviour though. You get the concept of functions taking arguments and returning a value. You also get error handling that climbs the stack in the same way that errors would in synchronous code.
In ES6 (the next version of JavaScript, partially supported by some of the newest browsers) you also get generators which helps you write even more synchronous seeming code.
Consider your readFile operation, if it were synchronous you could write something like:
function readJsonSync(filename) {
return JSON.parse(fs.readFileSync(filename, 'utf8'));
}
with the promise returning version in the current version of JavaScript you could write
function readJson(filename) {
return fs_readFile(filename, 'utf8').then(JSON.parse);
}
If you wanted to do the same with callbacks rathe than promises you'd have to write the following to get proper error handling behaviour:
function readJsonCallback(filename, callback){
fs.readFile(filename, 'utf8', function (err, res){
if (err) return callback(err)
try {
res = JSON.parse(res)
} catch (ex) {
return callback(ex)
}
callback(null, res)
})
}
With ES6 you can write:
var readJson = Q.async(function* (filename){
return JSON.parse(yield fs_readFile(filename, 'utf8'))
})
The yield "awaits" the promise and gives you the resolution value. The caveat is that the function that contains yield must be wrapped in Q.async and will return a promise itself.
If you want a more in depth look at this, or simply prefer videos to text, I gave a talk on this topic that you can find on YouTube: http://youtu.be/qbKWsbJ76-s
function fs_readFile (file, encoding) {
var deferred = Q.defer()
fs.readFile(file, encoding, function (err, data) {
if (err)
deferred.reject(err)
else
deferred.resolve(data)
})
return deferred.promise.then(console.log, console.error)
}
var promise=fs_readFile('myfile.txt','utf-8')