0

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?

ReFruity
  • 360
  • 2
  • 16
  • Source is my [discord bot](https://github.com/ReFruity/EzBot/blob/32-test-summon-to-the-wrong-channel-functionality/src/features/summon-to-the-channel.ts) – ReFruity Jun 04 '22 at 07:11
  • 2
    Timeouts are often implemented with `return Promise.race([p1, p2])` where you have a race between two promises, one triggered by a timeout and one triggered by your main operation. Whichever finishes first wins the race and becomes the resolved value that `Promise.race()` resolves/rejects to. – jfriend00 Jun 04 '22 at 07:18

0 Answers0