Skip to content

Change default async generator component behavior to pause at yields #291

@brainkim

Description

@brainkim

Summary

This issue proposes a breaking change to async generator components: their default behavior will be changed so that, when not in a props loop (for {} of this, for await {} of this), they pause at the first yield until rerendered, rather than continuously resuming. Currently, the default behavior is to resume as though the component is in a for await...of loop. This is a continuation of the effort started in #243 to align the behavior of async generator components with sync generator components to make them more predictable.

Motivation

  • Ensures components respond to props and state changes.
  • Discourage while (true) components.
  • Reduces confusion for developers refactoring between sync and async generator components.
  • Makes continuous polling/timer logic explicit and easier to reason about.
  • Async generator components which use for...of and initial yields work exactly the same as sync generator components:
    async function *Component() {
      // currently, this yield resumes immediately,
      // but it would be better to behave like sync generator components
      yield initial;
      for ({} of this) {
        yield nonInitial;
      }
    }
  • In 0.7, the result passed back into async generator components is always a promise, but if the sync gen behavior is used, we can pass back in the actual value because we block on children.

Breaking Change

  • Components that previously relied on automatic continuous resuming (outside of loops) will now need explicit refresh calls or should use a for await...of loop for continuous updates.
  • Migration guidance should be provided in release notes.
  • Might be alleviated with the addition of an isUnmounted property on contexts.

Example:

// Old behavior: this component would continuously resume
// New behavior: pauses at first yield until rerendered
async function *Timer() {
  let i = 0;
  while (true) {
    await new Promise((r) => setTimeout(r, 1000));
    yield <div>{i}</div>
  }
}

Action Items

  • Update async generator component logic to pause at first yield when not in a loop.
  • Document migration steps and highlight affected patterns in release notes.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions