ishes, control is returned to function
a
. When
c
is executing, therefore, neither
a
nor
b
is “done.” This nesting of functions that are not done is called the call stack.
If an error occurs in
c
, then, what happens to
a
and
b
? As it happens, it causes an
error in
b
(because
b
may rely on the return of
c
), which in turn causes an error in
a
(because
a
may rely on the return of
b
). Essentially, the error propagates up the call
stack until it’s caught.
Errors can be caught at any level in the call stack; if they aren’t caught, the JavaScript
interpreter will halt your program unceremoniously. This is called an unhandled
exception or an uncaught exception, and it causes a program to crash. Given the num‐
ber of places errors can occur, it’s difficult and unwieldy to catch all possible errors,
which is why programs crash.
When an error is caught, the call stack provides useful information in diagnosing the
problem. For example, if function
a
calls function
b
, which calls function
c
, and the
error occurs in
c
, the call stack tells you that not only did the error occur in
c
, it
occurred when it was called by
b
when
b
was called by
a
. This is helpful information if
function
c
is called from many different places in your program.
In most implementations of JavaScript, instances of
Error
contain a property
stack
,
which is a string representation of the stack (it is a nonstandard feature of JavaScript,
but it is available in most environments). Armed with this knowledge, we can write
an example that demonstrates exception handling:
function
a
() {
console
.
log
(
'a: calling b'
);
b
();
console
.
log
(
'a: done'
);
}
function
b
() {
console
.
log
(
'b: calling c'
);
c
();
console
.
log
(
'b: done'
);
}
function
c
() {
console
.
log
(
'c: throwing error'
);
throw
new
Error
(
'c error'
);
console
.
log
(
'c: done'
);
}
function
d
() {
console
.
log
(
'd: calling c'
);
c
();
console
.
log
(
'd: done'
);
}
try
{
a
();
}
catch
(
err
) {
172 | Chapter 11: Exceptions and Error Handling