Skip to content

Instantly share code, notes, and snippets.

@nikkaroraa
Created June 14, 2017 03:22
Show Gist options
  • Save nikkaroraa/f3050ec35ee20ff86fba8cebeb959350 to your computer and use it in GitHub Desktop.
Save nikkaroraa/f3050ec35ee20ff86fba8cebeb959350 to your computer and use it in GitHub Desktop.
Understanding callback functions in Javascript
Callback functions are extremely important in Javascript. They’re pretty much everywhere.
If you’re coming from a background in functional programming, or you have experience in writing concurrent programs in other languages, you might not have any difficulty understanding why callback functions are used the way they are in Javascript programs, but I’ve talked to more than one programmer who found the concept very unintuitive.
Functions are objects
To understand callback functions you first have to understand regular functions. This might seen like a “duh” thing to say, but functions in Javascript are a bit different than (for example) methods in Java. Functions in Javascript are first-class, and are actually objects.
First-class functions: In computer science, a programming language is said to have first-class functions if it treats functions as first-class citizens. Specifically, this means the language supports passing functions as arguments to other functions, returning them as the values from other functions, and assigning them to variables or storing them in data structures.
Functions in JS are actually Function Objects: In JavaScript, functions are first-class objects, because they can have properties and methods just like any other object. What distinguishes them from other objects is that functions can be called. In brief, they are Function Objects.
A callback function is a function which is:
passed as an argument to another function
is invoked after some kind of event
once its parent function completes, the function passed as an argument is then called
How to explain callbacks in plain english?
A callback is any function that is called by another function, which takes the first function as a parameter
Consider how programmers normally write to a file:
fileObject = open(file)
//now that we have WAITED for the file to open, we can write to it
fileObject.write("We are writing to the file.")
//now we can continue doing the other, totally unrelated things our program does
In the above example, we wait for the file to open, before we write to it.
This blocks the flow of execution, and our program cannot do any of the other things it might need to do
This is where callbacks are useful:
//we pass writeToFile (a callback function) to the open function
fileObject = open(file, writeToFile)
//execution continues flowing -- we don't wait for the file to be opened
//once the file is opened we write to it, but while we wait we can do other things
JavaScript Promises
Normally code is synchronous - one statement executes and there is a guarantee that the next statement will execute immediately afterwards
With asynchronous operations, you should assume that you have no idea when the operation will complete. You can't even assume that just because you send out one request first, and another request second, that they will return in that order
Callbacks are the standard way of handling asynchrnous code in JavaScript, but promises are the best way to handle asynchronous code. This is because callbacks make error handling difficult, and lead to ugly nested code.
var p = new Promise(function(resolve, reject) {
// Do an async task async task and then...
if(/* good condition */) {
resolve('Success!');
}
else {
reject('Failure!');
}
});
p.then(function() {
/* do something with the result */
}).catch(function() {
/* error :( */
})
Realistic example:
A realistic example of using promises would be converting a get request to a promise-based task:
// From Jake Archibald's Promises and Back:
// http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promisifying-xmlhttprequest
function get(url) {
// Return a new promise.
return new Promise(function(resolve, reject) {
// Do the usual XHR stuff
var req = new XMLHttpRequest();
req.open('GET', url);
req.onload = function() {
// This is called even on 404 etc
// so check the status
if (req.status == 200) {
// Resolve the promise with the response text
resolve(req.response);
}
else {
// Otherwise reject with the status text
// which will hopefully be a meaningful error
reject(Error(req.statusText));
}
};
// Handle network errors
req.onerror = function() {
reject(Error("Network Error"));
};
// Make the request
req.send();
});
}
// Use it!
get('story.json').then(function(response) {
console.log("Success!", response);
}, function(error) {
console.error("Failed!", error);
});
One benefit of this function-as-object concept is that you can pass code to another function in the same way you would pass a regular variable or object (because the code is literally just an object).
Passing a function as a callback
Passing a function as an argument is easy
// define our function with the callback argument
function some_function(arg1, arg2, callback) {
// this generates a random number between
// arg1 and arg2
var my_number = Math.ceil(Math.random() * (arg1 - arg2) + arg2);
// then we're done, so we'll call the callback and
// pass our result
callback(my_number);
}
// call the function
some_function(5, 15, function(num) {
// this anonymous function will run when the
// callback is called
console.log("callback called! " + num);
});
It might seem silly to go through all that trouble when the value could just be returned normally, but there are situations where that’s impractical and callbacks are necessary.
Don’t block the way
Traditionally functions work by taking input in the form of arguments and returning a value using a return statement (ideally a single return statement at the end of the function: one entry point and one exit point). This makes sense. Functions are essentially mappings between input and output.
Javascript gives us an option to do things a bit differently. Rather than wait around for a function to finish by returning a value, we can use callbacks to do it asynchronously. This is useful for things that take a while to finish, like making an AJAX request, because we aren’t holding up the browser. We can keep on doing other things while waiting for the callback to be called. In fact, very often we are required (or, rather, strongly encouraged) to do things asynchronously in Javascript.
It is not necessary that a function which contains an asynchronous function must itself have a callback.
It is totally based on our choice. When it is done, the main motive behind it is to make sure that:
-First, the child asynchronous function returns something.
-After the child function returns, the parent function also returns something to make sure a complete thorough exit from the function.
Think of callbacks as an alternative to return statements in traditional functional paradigm;
--- Asynchronous function needs to perform an asynchronous operation
For a function to be asynchronous it needs to perform an asynchronous operation. It needs to incorporate the argument callback in handling the results of this asynchronous operation. Only this way the function becomes asynchronous.
For example a function reading contents of a file with the correct encoding can be implemented as an asynchronous function.
1 function readFileAsUtf8(filename, callback)
2 fs.readFile(filename, "utf8", function(err, data) => {
3 callback(data);
4 });
5 }
Your callback will not be called as part of the execution of readFileAsUtf8 function. It will be called only after the file contents have been read at some point in the future. It will be called asynchronously.
Throwing an exception from your callback would reveal a short stack trace originating from the event loop. It would not contain any mention of the function that originally took the function as an argument.
Refer: https://bytearcher.com/articles/does-taking-a-callback-make-a-function-asynchronous/
Function callbacks make a code look like a spaghetti code, also known as callback hell.
Which is why we use PROMISES.
What even is a promise?
I’ll keep this brief, since it’s been covered extensively elsewhere.
A promise is a special kind of javascript object which contains another object. I could have a promise for the integer 17, or the string “hello world”, or some arbitrary object, or anything else you could normally store in a javascript variable.
How do I access the data in a promise? I use `.then()`:
function getFirstUser() {
return getUsers().then(function(users) {
return users[0].name;
});
}
How do I catch the errors from a promise chain? I use `.catch()`
function getFirstUser() {
return getUsers().then(function(users) {
return users[0].name;
}).catch(function(err) {
return {
name: 'default user'
};
});
}
Even though promises will usually be for ‘future’ data, once I actually have a promise for something, I really don’t need to care whether the data will be there in future, or it’s already been retrieved. I just call `then()` in either case. As such, promises force consistent asynchronicity (and avoid releasing zalgo). It’s like saying, ‘this is going to be an async function, whether or not the return value is available now or later’.
Refer: https://gist.github.com/amysimmons/3d228a9a57e30ec13ab1
https://medium.com/@bluepnume/learn-about-promises-before-you-start-using-async-await-eb148164a9c8
http://recurial.com/programming/understanding-callback-functions-in-javascript/
http://callbackhell.com/
http://javascriptissexy.com/understand-javascript-callback-functions-and-use-them/
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment