They sound difficult, but let?s take a moment to explain why they?re not
She?s calling, it?s totally related.
If you?re starting out with Node.js you?ll quickly bump into callbacks. They?re such a popular concept that you?ve already heard about them, but maybe hadn?t thought much about them yet. Well, now you?re here ? hi!
Before we define callbacks, we need to understand why they even exist. They exist because of Node?s asynchronous nature. What everybody knows about asynchronous programming is that it?s ?better? but ?harder?.
OK, That Last Sentence is Slightly Over-Simplistic
Let?s dive in a little deeper and compare code written in a synchronous fashion with its asynchronous counterpart.
What you?ll notice right away is that the asynchronous version is?ugly. It does look like we?re making things unnecessarily complex for little gain, so let?s talk about the gains right away, before you decide to pack JS in and go back to PHP!
The cool thing about asynchronous programming is that while your code waits for something to be done (like an API call or a response from a mystic and far away database) it can do something else. In other words, your code doesn?t get blocked when a process is taking a long time. This is actually the main reason why Node.js was even created: servers running synchronous code spend a lot of time waiting. If servers can process requests while they are waiting for I/O, stuff gets done faster (I/O stands for input/output by the way!)
Things do feel a little more complex with asynchronous programming, especially when you begin, but it?s actually not that hard a concept to grasp and the benefits are worth it.
With a better understanding of terms like ?asynchronous programming? and ?non-blocking?, let?s answer a simple question.
What Are Callbacks?
A Callback is simply a function passed as an argument to another function which will then use it (call it back).
Here is a simple, yet bold, example of a callback function.
fs.readFile(funFileName, function(err, file) { if(err) handleError(err); console.log(“file: “, file)})
When fs.readFile is done fetching the file funFileName, it executes the callback function, which handles the error if an error is thrown and logs the retrieved file to the console.
Note that the callback function is taking 2 arguments: err and file. By convention, the first argument of a callback function is an error. If an error is thrown by the parent function, it will be there for you to deal with and if no error is thrown ? that happens sometimes ? then the first argument should be null. Also by convention, the following arguments are the response data. Look up Error-First Callbacks for more on this.
That is all well and good, but why not simply write the above like this:
let file = fs.readFile(fooFileName);console.log(“file: “, file);
In short, because async.
In this second example, file will be undefined when we try to log it, because fs.readFile won?t be done fetching before we get to the console.log().
Let me reassure you, this:
let file = 1+1;console.log(“file: “, file);
will work.
It feels a little confusing to newcomers who have only dealt with synchronous programming; you?ve lived a happy life filled with love and logic, then suddenly, line 3 could get executed before line 2!? Should you just use callbacks everywhere all the time just to make sure? No.
Callbacks are to be used when we don?t know when something will be done. Again, think of something like an API call, fetching data from a database or I/O with the hard drive. All of these will take time, so we want our callback to be called when the event we are waiting for is done. Hence the term event-driven programming.
If you feel like you only kind of get it, don?t feel lonely. Like many things in life, it takes a bit of hands-on experience to really get it.
Before you go on to get that hands-on experience, let me introduce you to the next mess you will get into.
Callback Hell
Don?t study the code below too thoroughly, just look at it:
Ew. This is probably not what you want your code to look like, unless you really love triangles.
Code that looks like the above has been named Callback Hell. Async JS doesn?t need to look like this, but it?s easy to carelessly write a few functions and realize you?re deep in callback hell. Again.
Explaining the many strategies you can use to write cleaner asynchronous code is slightly out of the scope of this introductory article, but here?s a sweet link to get some ideas when you?re ready: callbackhell.com.
TL;DR
Callbacks are functions. You pass them to other functions so they can be executed when the time is right, i.e. when the event needed by the callback has happened. This way of doing things comes with asynchronous programming, which is not that hard and is worth it.
Coming soon?
If you?re starting out with callbacks you?ll quickly bump into promises, you should probably make sure you really get callbacks before you jump on promises, but you also should probably get to know promises some day. So stay tuned to Better Programming to make sure you catch the upcoming article about Promises!