import { Awaitable } from 'justypes' import { isAsyncIterable } from '@src/is-async-iterable.js' import { GetTypeOfIterable } from './utils.js' enum Kind { Sync , Async } export type ExtractTypeTupleFromAsyncLikeIterableTuple = { [K in keyof T]: T[K] extends AsyncIterable> ? Awaited : T[K] extends Iterable> ? Awaited : GetTypeOfIterable } export function zipAsync< T , U extends Array> | AsyncIterable>> >( iterable: Iterable> | AsyncIterable> , ...otherIterables: U ): AsyncIterableIterator< [Awaited, ...ExtractTypeTupleFromAsyncLikeIterableTuple] > { return _zipAsync( iterable , ...otherIterables ) as AsyncIterableIterator< [Awaited, ...ExtractTypeTupleFromAsyncLikeIterableTuple] > } async function* _zipAsync( ...iterables: Array> | AsyncIterable>> ): AsyncIterableIterator>> { const length = iterables.length const iterators = iterables.map(iterable => { if (isAsyncIterable(iterable)) { return [Kind.Async, iterable[Symbol.asyncIterator]()] as const } else { return [Kind.Sync, iterable[Symbol.iterator]()] as const } }) const dones = iterators.map(() => false) try { while (true) { const result = new Array>(length) for (let i = 0; i < length; i++) { const [kind, iterator] = iterators[i] let temp: IteratorResult> if (kind === Kind.Async) { temp = await (iterator as AsyncIterator).next() } else { temp = (iterator as Iterator>).next() } if (temp.done) { dones[i] = true return } result[i] = await temp.value } yield result } } finally { const undoneIterators = iterators.filter((_, i) => !dones[i]) for (const [kind, iterator] of undoneIterators) { if (kind === Kind.Async) { await (iterator as AsyncIterator).return?.() } else { (iterator as Iterator).return?.() } } } }