UNPKG

loop-controls

Version:

break/continue controls for loops and higher-order functions (sync, async, concurrent). Designed to compose with ts-pattern.

75 lines (68 loc) 2.32 kB
import { _Break as BaseBreak, _Continue as BaseContinue, type Control } from "./core.js"; export async function forEachAsync<T>( iter: AsyncIterable<T> | Iterable<T>, fn: (item: T, control: Control, index: number) => Promise<void> ): Promise<{ broken: false } | { broken: true }> { class LoopBreak<T = unknown> extends BaseBreak<T> {} class LoopContinue extends BaseContinue {} const $: Control = { break: (value?: unknown): never => { throw new LoopBreak(value); }, continue: (): never => { throw new LoopContinue(); }, }; let i = 0; for await (const item of iter) { try { await fn(item, $, i++); } catch (e) { if (e instanceof LoopContinue) continue; if (e instanceof LoopBreak) return { broken: true }; throw e; } } return { broken: false }; } export async function reduceAsync<T, A>( iter: AsyncIterable<T> | Iterable<T>, seed: A, fn: (acc: A, item: T, control: Control, index: number) => Promise<A> ): Promise<A> { class LoopBreak<T = unknown> extends BaseBreak<T> {} class LoopContinue extends BaseContinue {} const $: Control = { break: (value?: unknown): never => { throw new LoopBreak(value); }, continue: (): never => { throw new LoopContinue(); }, }; let acc = seed, i = 0; for await (const item of iter) { try { acc = await fn(acc, item, $, i++); } catch (e) { if (e instanceof LoopContinue) continue; if (e instanceof LoopBreak) return ((e as LoopBreak).value !== undefined ? (e as LoopBreak).value : acc) as A; throw e; } } return acc; } export async function findAsync<T>( iter: AsyncIterable<T> | Iterable<T>, fn: (item: T, control: Control, index: number) => Promise<boolean> ): Promise<T | undefined> { class LoopBreak<T = unknown> extends BaseBreak<T> {} class LoopContinue extends BaseContinue {} const $: Control = { break: (value?: unknown): never => { throw new LoopBreak(value); }, continue: (): never => { throw new LoopContinue(); }, }; let i = 0; for await (const item of iter) { try { if (await fn(item, $, i++)) return item; } catch (e) { if (e instanceof LoopContinue) continue; if (e instanceof LoopBreak) return (e as LoopBreak).value as T | undefined; throw e; } } return undefined; }