Skip to content

Conversation

@Refzlund
Copy link
Owner

@Refzlund Refzlund commented Nov 27, 2025

Summary

Fixes #17 - Returning a Promise from an async generator in func wraps the Promise instead of awaiting it.

Problem

When using func with an async generator (async function*) and returning a Promise at the end (e.g., from another function call), the returned value became Result<Promise<T>> instead of Result<T>. The Promise was not awaited before being wrapped in the Result.

const inner = func(async () => {
  return { id: 1 }
})

const outer = func(async function*() {
  return inner() // Returns Promise<Result<{ id: 1 }>>
})

const result = await outer()
// Before fix: Result.Ok with value Promise<Result<{ id: 1 }>> ❌
// After fix: Result.Ok with value { id: 1 } ✅

Root Cause

In the handleIteration function within Result.func, when an async generator completed (iteration.done === true), the code checked if the value was a Result instance. When iteration.value was a Promise, it passed the instanceof Result check as false, so it got wrapped in Result.ok(Promise) instead of being awaited first.

Solution

Modified the async generator iteration path to check if the final value is a Result.Ok containing a Promise. When detected:

  1. Await the Promise
  2. If the awaited value is itself a Result, return it directly
  3. Otherwise, wrap the awaited value in Result.ok()
  4. Handle any errors during Promise resolution with toThrownError

Changes

  • src/result.ts: Added Promise detection and awaiting logic in the async generator completion handler
  • tests/issues/#17-async-generator-promise-wrap.test.ts: Minimal reproduction test for issue fix: func(async) infinite recursion in function execution #8
  • tests/promises.test.ts: Comprehensive test suite covering Promise handling in async generators

Test Results

  • ✅ All 312 tests pass
  • ✅ TypeScript type checking passes
  • ✅ Linting passes

Workaround (no longer needed)

Previously, users had to use yield* await instead of directly returning the Promise:

// Old workaround (still works, but no longer required)
const processData = func(async function*() {
  const data = yield* await fetchData()
  return data
})

// Now this works correctly too
const processData = func(async function*() {
  return fetchData() // Promise is now properly awaited
})

@changeset-bot
Copy link

changeset-bot bot commented Nov 27, 2025

🦋 Changeset detected

Latest commit: f76713a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
xult Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@Refzlund Refzlund merged commit be2e3c1 into release Nov 27, 2025
1 check passed
@Refzlund Refzlund deleted the fix-async-generator-await-promise branch November 27, 2025 10:11
@github-actions github-actions bot mentioned this pull request Nov 27, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: returning a Promise from an async generator in func wraps the Promise instead of awaiting it

1 participant