function
addTimeout
(
fn
,
timeout
) {
if
(
timeout
===
undefined
)
timeout
=
1000
;
// default timeout
return
function
(
...
args
) {
return
new
Promise
(
function
(
resolve
,
reject
) {
const
tid
=
setTimeout
(
reject
,
timeout
,
new
Error
(
"promise timed out"
));
fn
(
...
args
)
.
then
(
function
(
...
args
) {
clearTimeout
(
tid
);
resolve
(
...
args
);
})
.
catch
(
function
(
...
args
) {
clearTimeout
(
tid
);
reject
(
...
args
);
});
});
}
}
If you’re saying “Whoa…a function that returns a function that returns a promise
that calls a function that returns a promise…my head is spinning!”, I can’t blame you:
to add a timeout to a promise-returning function is not trivial, and requires all of the
preceding contortions. Completely understanding this function is left as an advanced
reader’s exercise. Using this function, however, is quite easy: we can add a timeout to
any function that returns a promise. Let’s say our very slowest rocket attains orbit in
10 seconds (isn’t future rocket technology great?), so we set a timeout of 11 seconds:
c
.
go
()
.
then
(
addTimeout
(
launch
,
4
*
1000
))
.
then
(
function
(
msg
) {
console
.
log
(
msg
);
})
.
catch
(
function
(
err
) {
console
.
error
(
"Houston, we have a problem: "
+
err
.
message
);
});
Now our promise chain will always settle, even when the
launch
function behaves
badly.
Generators
, generators allow two-way communication
between a function and its caller. Generators are synchronous in nature, but when
combined with promises, they offer a powerful technique for managing async code in
JavaScript.
Let’s revisit the central difficulty with async code: it’s harder to write than synchro‐
nous code. When we tackle a problem, our minds want to approach it in a synchro‐
nous fashion: step 1, step 2, step 3, and so on. However, that can have performance
212 | Chapter 14: Asynchronous Programming