It looks a lot better than callback hell, doesn’t it? It’s also neater than promises alone.
It flows the way we think. Running it is simple:
grun
(
theFutureIsNow
);
One Step Forward and Two Steps Back?
You might (quite reasonably) be thinking that we’ve gone to so much trouble to
understand asynchronous execution, then make it easier…and now we’re right back
where we started, except with the extra complication of generators and converting
things to promises and
grun
. And there is some truth in this: in our
theFutureIsNow
function, we have somewhat thrown the baby out with the bathwater. It’s easier to
write, easier to read, and we’re reaping some of the benefits of asynchronous execu‐
tion, but not all of them. The sharp question here is: “Would it be more efficient to
read the three files in parallel?” The answer to that question depends a lot on the
problem, the implementation of your JavaScript engine, your operating system, and
your filesystem. But let’s put aside those complexities for a moment, and recognize
that it doesn’t matter what order we read the three files in, and it’s conceivable that
efficiency would be gained from allowing those file read operations to happen in par‐
allel. And this is where generator runners can lull us into a false sense of compla‐
cency: we wrote the function this way because it seemed easy and straightforward.
The problem (assuming there is a problem) is easy to solve.
Promise
provides a
method called
all
, which resolves when all the promises in an array resolve…and
will execute the asynchronous code in parallel if possible. All we have to do is modify
our function to use
Promise.all
:
function*
theFutureIsNow
() {
const
data
=
yield
Promise
.
all
([
nfcall
(
fs
.
readFile
,
'a.txt'
),
nfcall
(
fs
.
readFile
,
'b.txt'
),
nfcall
(
fs
.
readFile
,
'c.txt'
),
]);
yield
ptimeout
(
60
*
1000
);
yield
nfcall
(
fs
.
writeFile
,
'd.txt'
,
data
[
0
]
+
data
[
1
]
+
data
[
2
]);
}
The promise returned by
Promise.all
provides an array containing the fulfillment
value of each promise in the order they appear in the array. Even though it’s possible
for c.txt to be read before a.txt,
data[0]
will still hold the contents of a.txt, and
data[1]
will still hold the contents of c.txt.
Your takeaway from this section should not be
Promise.all
(though that’s a handy
tool to know about); your takeaway should be to consider what parts of your program
can be run in parallel, and what parts can’t. In this example, it’s even possible that the
timeout could be run in parallel to the file reads: it all depends on the problem you’re
trying to solve. If what’s important is that the three files are read and then 60 seconds
Generators | 215