4.07.2013

Callbacks

Version en espaƱol
In javascript, callbacks are a complicated idea and for someone new to javascript, the idea causes significant confusion.

First - what is a callback?

A callback is the call that the system does in return when a process we started has yet to finish, and we need that process call to do something specific. Wikipedia gives a pretty good definition of this.  We usually do a function that returns something in a synchronous way, for example
function turnOnPc() {
    //more stuff
    if (error) {
        return false;
    } else {
        return true;
    }
}

function doSomethingWithPc(isOn) {
    //do something
}

var isOn = turnOnPc();
doSomethingWithPc(isOn);
//other functions
In this case, we just call the function "turnOnPC" and we wait for the function to finish processing, looking for  areturn as to whether or not the pc turned on correctly or not.  But, what happens if we have a slow or really old pc? Sitting and waiting won't be effective here.

Asynchronous programming

Asynchronous programming refers to the ability to call a function without the need of waiting for it to end.  In javascript it's very common to use this.  Javascript allows us to send functions as parameters to the functions... wait, what??

In javascript functions behave a little bit differently than in other programming languages.  For more information about functions in general, check out the MDN documentation. Back to our example

function turnOnPc(callback) {
 //more stuff
 if (error) {
  callback(error, false);
 } else {
  callback(null, true);
 }
}

function doSomethingWithPc(error, isOn) {
 //do something
}

turnOnPc(doSomethingWithPc);
//other functions

Usually we pass variables as arguments to other functions, which you can also easily do in js.  In this case we are sending a function as an argument to "turnOnPc" and at the end of the function it calls back the function we sent with some parameters "callback()".  In this particular case we are using a common pattern found in node.js where we always send the error variable as a first parameter and then all the other data variables as needed.

In the trivial example of turning on a PC, imagine having to wait to go get a drink of water until your PC finished booting.  Asyncronous calls allow you to go get a drink of water while your PC boots.  We don't have to wait for "turnOnPc" to end, when "turnOnPc" ends it will call the function we sent it and then follow with the functionality necessary.

Callback Hell

Its very common to be entangled in several functions that needs a callback, lets look to our example expanded

function turnOnPc(callback) {
 //more stuff
 if (error) {
  callback(error, false);
 } else {
  callback(null, true);
 }
}
turnOnPc(function (error, isOn) {
 if (!isOn) {
  drinkWater(function () {
   //drinking water
   doSomethingElse(function () {
    //...
   });
  });
 }
});

To avoid long lists of nested callbacks, generate functions with names and don't send anonymous functions.  Even better yet, use "promises" to do that.

Where i have seen them??!!

One of the most common uses for callbacks in javascript is calling AJAX or DOM events.  In node.js usually it's used for complex calls, slow methods, or file/network access.  Here is an easy example with jquery


$('element').on('click', function(event) {
 //hacer algo con el elemento(this)/evento(event)
});

In this case, we are binding the function to the click event of the element "element".  You can read this as  when the user clicks on "element" run this function.  You are not necessarily on the screen doing nothing, but you are waiting for the event to happen to run the function while the page is still working without blocking the page.

Another common example is ajax


$.ajax({
  type: "POST",
  url: "some.php",
  data: { name: "John", location: "Boston" }
}).done(function( msg ) {
  alert( "Data Saved: " + msg );
});

Although jquery already uses promises, we can use this as an example.  Here we are calling some service, and when that service responds we run the function inside "done"

Node.js uses callbacks for many things. ffor example, when we are loading a file we can do it synchronously or asynchronously.  If we do it synchronously the thread will stop until the file is loaded completely which is not a good way to do it.  But, if we do it asynchronously, other functions will run while the file ends loading

fs.readFile('data.txt', function (err, data) {
  if (err)
    throw err;
  if (data)
    console.log(data.toString('utf8'));
});

The function takes the file path and a callback.  When the function finishes loading the file it calls the callback.

What happens with this?!!

"This" in javascript its the context in which we are currently running, in the browser.  Without being inside a function specifically, the context is "window" which is the global object.  When you are inside a callback, "this" can change.  For example when you are listening for an event with jquery, "this" is the DOM element that you have listening to the event

//here this is window
var parentwindow = this;
$('element').on('click', function(event) {
 //do something with the element(this)/event
 //here this, its the element (e.g. an "a" tag)
 var element = this;
});

You can send a different context for the callback using apply or call, in our initial example it would be

function turnOnPc(callback) {
 //more stuff
 if (error) {
  callback.apply(othercontext, error, false);
 } else {
  callback.apply(othercontext, null, true);
 }
}

This way the first parameter will be "this" in the callback

Questions, etc, put them in the comments :)

No comments:

Post a Comment