consequences, which is why async exists. Wouldn’t it be nice if you could have the
performance benefits of async without the additional conceptual difficulty? That’s
where generators can help us.
Consider the “callback hell” example we used previously: reading three files, delaying
for one minute, then writing the contents of the first three files out to a fourth file.
How our human minds would like to write this is something like this pseudocode:
dataA = read contents of 'a.txt'
dataB = read contents of 'b.txt'
dataC = read contents of 'c.txt'
wait 60 seconds
write dataA + dataB + dataC to 'd.txt'
Generators enable us to write code that looks very much like this…but the function‐
ality doesn’t come out of the box: we’ll have to do a little work first.
The first thing we need is a way to turn Node’s error-first callbacks into promises.
We’ll encapsulate that into a function called
nfcall
(Node function call):
function
nfcall
(
f
,
...
args
) {
return
new
Promise
(
function
(
resolve
,
reject
) {
f
.
call
(
null
,
...
args
,
function
(
err
,
...
args
) {
if
(
err
)
return
reject
(
err
);
resolve
(
args
.
length
<
2
?
args
[
0
]
:
args
);
});
});
}
This function is named after (and based on) the nfcall method in
. If you need this functionality, you should
probably use Q. It includes not only this method, but many more
helpful promise-related methods as well. I present an implementa‐
tion of nfcall here to demonstrate that there is no “magic.”
Now we can convert any Node-style method that takes a callback to a promise. We’ll
also need
setTimeout
, which takes a callback…but because it predates Node, it wasn’t
hip to the error-first convention. So we’ll create
ptimeout
(promise timeout):
function
ptimeout
(
delay
) {
return
new
Promise
(
function
(
resolve
,
reject
) {
setTimeout
(
resolve
,
delay
);
});
}
The next thing we’ll need is a generator runner. Recall that generators are not inher‐
ently asynchronous. But because generators allow the function to communicate to the
caller, we can create a function that will manage that communication—and know
how to handle asynchronous calls. We’ll create a function called
grun
(generator run):
Generators | 213