Imagine we have an async generator function:
async f * (connection) {
while (true) {
...
await doStuff()
yield value
}
}
Suppose that this function is virtually endless and gives us results of some async actions. We want to iterate these results:
for await (const result of f(connection)) {
...
}
Now imagine we want to break out of this for-await
loop when some timeout ends and clean things up:
async outerFunc() {
setTimeout(() => connection.destroy(), TIMEOUT_MS)
for await (const result of f(connection)) {
...
if (something) {
return 'end naturally'
}
}
}
Assume that connection.destroy()
ends the execution of f
and ends the for-await
loop. Now it would be great to return some value from the outerFunc
when we end by timeout. The first thought is wrapping in a Promise
:
async outerFunc() {
return await new Promise((resolve, reject) => {
setTimeout(() => {
connection.destroy()
resolve('end by timeout')
}, TIMEOUT_MS)
for await (const result of f(connection)) { // nope
...
if (something) {
resolve('end naturally')
}
}
})
}
But we cannot use awaits
inside Promise
and we cannot make the function async
due to this antipattern
The question is: how do we return by timeout the right way?
It gets much easier, if you use an existing library that can handle asynchronous generators and timeouts automatically. The example below is using library iter-ops for that: