const
fs
=
require
(
'fs'
);
const
fname
=
'may_or_may_not_exist.txt'
;
fs
.
readFile
(
fname
,
function
(
err
,
data
) {
if
(
err
)
return
console.error(
`error reading file
${
fname
}
:
${
err
.
message
}
`
);
console.log(
`
${
fname
}
contents:
${
data
}
`
);
});
The first thing we do in the callback is see if
err
is truthy. If it is, there was an issue
reading the file, and we report that to the console and immediately return
(
console.error
doesn’t evaluate to a meaningful value, but we’re not using the return
value anyway, so we just combine it into one statement). This is probably the most
often overlooked mistake with error-first callbacks: the programmer will remember
to check it, and perhaps log the error, but not return. If the function is allowed to
continue, it may rely on the callback having been successful, which it wasn’t. (It is
possible, of course, that the callback doesn’t completely rely on success, in which case
it may be acceptable to note the error and proceed anyway.)
Error-first callbacks are the de facto standard in Node development (when promises
aren’t being used), and if you’re writing an interface that takes a callback, I strongly
advise you to adhere to the error-first convention.
Callback Hell
While callbacks allow you to manage asynchronous execution, they have a practical
drawback: they’re difficult to manage when you need to wait on multiple things
before proceeding. Imagine the scenario where you’re writing a Node app that needs
to get the contents of three different files, then wait 60 seconds before combining the
contents of those files and writing to a fourth file:
const
fs
=
require
(
'fs'
);
fs
.
readFile
(
'a.txt'
,
function
(
err
,
dataA
) {
if
(
err
)
console
.
error
(
err
);
fs
.
readFile
(
'b.txt'
,
function
(
err
,
dataB
) {
if
(
err
)
console
.
error
(
err
);
fs
.
readFile
(
'c.txt'
,
function
(
err
,
dataC
) {
if
(
err
)
console
.
error
(
err
);
setTimeout
(
function
() {
fs
.
writeFile
(
'd.txt'
,
dataA
+
dataB
+
dataC
,
function
(
err
) {
if
(
err
)
console
.
error
(
err
);
});
},
60
*
1000
);
});
});
});
This is what programmers refer to as “callback hell,” and it’s typified by a triangle-
shaped block of code with curly braces nested to the sky. Worse still is the problem of
204 | Chapter 14: Asynchronous Programming