function
grun
(
g
) {
const
it
=
g
();
(
function
iterate
(
val
) {
const
x
=
it
.
next
(
val
);
if
(
!
x
.
done
) {
if
(
x
.
value
instanceof
Promise
) {
x
.
value
.
then
(
iterate
).
catch
(
err
=>
it
.
throw
(
err
));
}
else
{
setTimeout
(
iterate
,
0
,
x
.
value
);
}
}
})();
}
grun
is based heavily on runGenerator, presented in Kyle Simpson’s
excellent
series of articles on generators
you read those articles as a supplement to this text.
This is a very modest recursive generator runner. You pass it a generator function,
and it runs it. As you learned in
, generators that call
yield
will pause until
next
is called on their iterator. This function does so recursively. If the iterator
returns a promise, it waits for the promise to be fulfilled before resuming the iterator.
On the other hand, if the iterator returns a simple value, it immediately resumes the
iteration. You may be wondering why we call
setTimeout
instead of just calling
iterate
directly; the reason is that we gain a little efficiency by avoiding synchronous
recursion (asynchronous recursion allows the JavaScript engine to free resources
more quickly).
You may be thinking “This is a lot of fuss!” and “This is supposed to simplify my life?”,
but the hard part is over.
nfcall
allows us to adopt the past (Node error-first callback
functions) to the present (promises), and
grun
allows us access to the future today
(expected in ES7 is the
await
keyword, which will essentially function as
grun
, with
an even more natural syntax). So now that we’ve got the hard part out of the way, let’s
see how all of this makes our life easier.
Remember our “wouldn’t it be nice” pseudocode from earlier in this chapter? Now we
can realize that:
function*
theFutureIsNow
() {
const
dataA
=
yield
nfcall
(
fs
.
readFile
,
'a.txt'
);
const
dataB
=
yield
nfcall
(
fs
.
readFile
,
'b.txt'
);
const
dataC
=
yield
nfcall
(
fs
.
readFile
,
'c.txt'
);
yield
ptimeout
(
60
*
1000
);
yield
nfcall
(
fs
.
writeFile
,
'd.txt'
,
dataA
+
dataB
+
dataC
);
}
214 | Chapter 14: Asynchronous Programming