A callback is a function which is passed as an argument to another function and invoked with the results generated after that function execution is completed. The result is propagated to the callback function instead of directly returning it.
A very simple example of a function directly returning results and the same with callback is shown in the snippet below.
Node.js makes use of callbacks as a way to notify about completion of asynchronous operations.Once an asynchronous operation is completed the callbacks are invoked with the results of the asynchronous operation.
The code snippet given below performs the following tasks:
- Download contents from a specified URL
- This is done using the Needle http client
- Make a directory if it does not exists
- This is done using mkdirp, the Node.js version of mkdir – p. Note, I have used the deprecated version 0.5.0 to use the callback for demonstration purposes, the latest versions use promises.
- Save the contents from the specified URL with a given file name
In the above code you can see that nesting of the if-else blocks and in-place callback definitions are making the code hard to read and maintain. This problem will increase further with complexity of the tasks and deeper the levels of nesting. This problem is referred to as the “Callback Hell” or “Pyramid of Doom” in JavaScript ( as the code takes the form of a pyramid due to deep levels of nesting).
This problem can be avoided by:
- Following the early return principle i.e. returning from the control flow as early as possible.
- Using named function definitions instead of in-place closures
- Making the code more modular by extracting re-usable code into separate functions.
The above code is refactored using the above guidelines. The tasks of directory checking / creation and writing to file is splitted into two named function as shown below:
The function downloadFile is now the entry point. All the functions returns in case of any error avoiding the if-else nesting which was used earlier.

