Skip to content Skip to sidebar Skip to footer

Understanding Function (err, Data) Callbacks

So I am confused about how the function(err, data) callback works, is the first argument always an error handler? What about the remaining arguments if you had something like func

Solution 1:

The caller of the callback (which is the readFile method in this case) decides what arguments are passed to the callback. You need to declare your callback to match what readFile says that it will pass to the callback. You can name the arguments anything you want (the names you use do not matter), but they will get values in the order that readFile decides.

In this case, fs.readFile() calls the callback with the two arguments you have in your code as in callback(err, data).

Here's an example from the node.js docs:

fs.readFile('/etc/passwd', function (err, data) {
  if (err) throw err;
  console.log(data);
});

Solution 2:

Easy to understand with an example... you can write your own version of a function that uses a callback, quite simply:

function getTwoStringsAsync(callback) {
    setTimeout(function() {
        callback("foo", "bar");
    }, 1000);
}

Here, the function getTwoStringsAsync is pre-supposing that "callback" is a function, and it implies signature (two parameters that are strings). The consumer of your method is free to adhere to the implied signature or not. It can use one or both of those values, or neither. But if it assumes any more than two parameters, those will appear as undefined.

getTwoStringsAsync(function() { console.log("done!"); });
getTwoStringsAsync(function(first, second) { console.log("got " + first + ", " + second); });
getTwoStringsAsync(function(first, second, third) { console.log(third); });   // "undefined"

It is arguably a limitation, or a strength, or weakly-typed languages like Javascript, that function signatures will not be verified. A strongly-typed language may generate a compiler error or warning in the first or third uses above.


Solution 3:

The arguments of the callback depend on how the callback gets called. For example:

var map = function(xs, f) {
  var result = [];
  for (var i=0; i<xs.length; i++) {
    // Here we call the callback function
    // with the element as context, the element as first param
    // and the index as second param.
    // Extra parameters are `undefined`
    result.push(f.call(xs[i], xs[i], i));
  }
  return result;
};

Then you'd use it like:

var addIdx = function(x,i){ return x+i };
map([1,2,3], addIdx); //=> [1,3,5]

Solution 4:

When you call a function it can either return a single value or throw a single error.

So the "equivalent" of this synchronous code:

try {
    var data = functionCall();
    //Use data
}
catch(err) {
    console.log(err);
}

Is this asynchronous callback:

functionCallAsync(function(err, data) {
    if (err) {
         console.log(err);
    }
    else {
         //Use data
    }

});

You can see now why it makes no sense to use multiple values in the callback. In fact, such pattern is actively harmful.

For example when using generators you are back to only having the possibility of using a single return value. But if some ignorant library author went ahead and broke the callback convention and used multiple, this would be impossible.

async(function* () {
    try {
        //What if `functionCallAsync` was called back with multiple values?
        var data = yield functionCallAsync();
        //Use data
    }
    catch(e) {
        console.log(e);
    }
});

Post a Comment for "Understanding Function (err, Data) Callbacks"