Skip to content

Async iterator promises are not resolved after stream end #387

Closed

Description

@reconbot

Given the following code, any promises for iterator results after the stream has ended will never resolve.

const a = new stream.PassThrough()
const b = a[Symbol.asyncIterator]()
const c = b.next()
const d = b.next()
a.end()
await c // { done: true, value: null }
await d // never resolves

if you repeat the same process for a generator function

const a = (async function* (){})()
const b = a[Symbol.asyncIterator]()
const c = b.next()
const d = b.next()
a.end()
await c // { value: undefined, done: true }
await d // { value: undefined, done: true }

Any promises from calls to next from an exhausted async generator function's iterator will immediately resolve as done: true.

I noticed this difference when trying to perform parallel async transforms on the values from a stream that resolved in a non deterministic order. I rely on all calls to next after the source has been exhausted to resolve, and since they don't for streams it hung forever.

I'm unsure of if this is defined in the async iterator spec, but it does seem like it might want to match behaviors of async generator functions.

Activity

  1. changed the title [-]Async iterator promises don't resolve after stream end[/-] [+]Async iterator promises doesn't resolve after stream end[/+] on Nov 26, 2018
  2. changed the title [-]Async iterator promises doesn't resolve after stream end[/-] [+]Async iterator promises are not resolved after stream end[/+] on Nov 26, 2018
  3. leobalter commented on Nov 26, 2018

    @leobalter

    from the ecmascript specs, next iterations should always resolve with the completion information. The behavior of your second example, using an ordinary async generator, is correct and works as expected.

  4. mcollina commented on Nov 27, 2018

    @mcollina
    SponsorMember

    Thanks! keep those coming, so we can move these out of experimental!

  5. mcollina commented on Nov 27, 2018

    @mcollina
    SponsorMember

    Here is the fix: nodejs/node#24668!

  6. reconbot commented on Nov 27, 2018

    @reconbot
    Author

    Any thoughts on the final value being null? It's not used in for/await/of because it only comes with the end, but I don't know the reasoning behind it.

  7. mcollina commented on Nov 27, 2018

    @mcollina
    SponsorMember

    Any thoughts on the final value being null? It's not used in for/await/of because it only comes with the end, but I don't know the reasoning behind it.

    What do you mean, I'm kind of lost.

    I noticed this difference when trying to perform parallel async transforms on the values from a stream that resolved in a non deterministic order. I rely on all calls to next after the source has been exhausted to resolve, and since they don't for streams it hung forever.

    Can you please expand on this?

  8. reconbot commented on Nov 27, 2018

    @reconbot
    Author

    The final iterator value of {value: null, done: true} doesn't match the async generator function's final value of {value: undefined, done: true} and I was just wondering if that was intentional. It doesn't effect using it in a for/await/of loop. Looks like you figured it out.

    I have a small library that has a transform function that is a parallel map over an async iterable's values. It's able to work a bit faster than a parallelMap function because it doesn't care about the order of the events. It queues up a bunch of promises for next(), I used a concurrency higher than the number of objects in a stream, which in turn asked for next() after the stream had ended, which is how I found this bug.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions