Node Tuts

Detach script window ⇗

You can navigate the video and the script by using the ← and → cursor keys.

The Callback Pattern

In Node, when you're performing an I/O operation like reading from a file, writing to a socket or making a database query, the function you're calling to initiate the I/O is not going to return the status or the value you're looking for. Instead, you have to provide a a function that will be called when that operation is completed.

One example is when you are reading the contents of a file into memory:

var fs = require('fs');

function callback(err, results) {
  if (err) return handleError(err);
  console.log('File contents:', results);
}

fs.readFile('/etc/passwd', callback);

In this example we are declaring a function named callback, which we are then passing as an argument to the function that will read the desired file content.

This function sometimes is declared inline like this:

var fs = require('fs');

fs.readFile('/etc/passwd', function (err, results) {
  if (err) return handleError(err);
  console.log('File contents:', results);
});

This style where you inline the callback functions, while less "tidy", is easier to read since the temporal dependence is clearer: first you initiate the readFile, then the callback is invoked.

Callback-last

Here you are witnessing something that is a defacto standard in Node and most of the third-party libraries:

The function call that initiates I/O accepts a callback function as last argument.

Another similar example is when you want to do a DNS lookup:

var dns = require('dns');

dns.lookup('google.com', function(err, ipAddress) {
  if (err) return handleError(err);
  console.log('google.com resolved to %s', ipAddress); 
});

Error-first

Another pattern that you may have noticed is that your callback function will be invoked with an error as the first argument if an error occurs. This is also a defacto pattern in Node.js and most third-party modules that use this pattern.

After you call the function that initiates I/O, that function must always call back your callback function exactly once — whether there is an error or if the operation completed successfully.

If an error occurred, the error will be passed in as the first argument, and you should not expect any additional argument to be filled in. You should handle the error — probably informing the user that an error occurred.

If there is no error, the first argument of the callback will contain a null or undefined value, which in any case you can test with a simple if statement like in this example:

fs.readFile('/etc/passwd', function (err, results) {
  if (err) return handleError(err);
  console.log('File contents:', results);
});

Here we're returning whatever the call to the handleError function returns because we don't really care what our callback function returns. Instead, if you prefer, we can be a little bit more verbose:

fs.readFile('/etc/passwd', function (err, results) {
  if (err) {
    handleError(err);
    return;
  }
  console.log('File contents:', results);
});