Understanding Function (err, Data) Callbacks
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"