UNPKG

shelving

Version:

Toolkit for using data in JavaScript.

74 lines (73 loc) 2.73 kB
import { getDeferred, getDelay } from "./async.js"; import { call, callMethod } from "./callback.js"; import { STOP } from "./constants.js"; /** * Is a value an async iterable object? * - Any object with a `Symbol.iterator` property is iterable. * - Note: Array and Map instances etc will return true because they implement `Symbol.iterator` */ export function isSequence(value) { return typeof value === "object" && !!value && Symbol.asyncIterator in value; } /** Infinite sequence that yields until a `SIGNAL` is received. */ export async function* repeatUntil(source, ...signals) { const iterator = source[Symbol.asyncIterator](); while (true) { const result = await Promise.race([iterator.next(), ...signals]); if (result === STOP) { await iterator.return?.(); // Make sure we call `return()` on the iterator because it might do cleanup. return STOP; } if (result.done) { return result.value; } yield result.value; } } /** Infinite sequence that yields every X milliseconds (yields a count of the number of iterations). */ export async function* repeatDelay(ms) { let count = 1; while (true) { await getDelay(ms); yield count++; } } /** Dispatch items in a sequence to a (possibly async) callback. */ export async function* callSequence(sequence, callback) { for await (const item of sequence) { call(callback, item); yield item; } } /** Pull values from a sequence until the returned function is called. */ export function runSequence(sequence, onNext, onError) { const { promise, resolve } = getDeferred(); void _runSequence(sequence[Symbol.asyncIterator](), promise, onNext, onError); return resolve; } async function _runSequence(sequence, stopped, onNext, onError) { try { const result = await Promise.race([stopped, sequence.next()]); if (!result) { // Stop iteration because the stop signal was sent. // Call `return()` on the iterator so it can perform any clean up. callMethod(sequence, "return"); return; } if (result.done) { // Stop iteration because iterator is done. // Don't need to call `return()` on the iterator (assume it stopped itself when it sent `done: true`). return; } // Forward the value to the next callback. if (onNext) call(onNext, result.value); } catch (thrown) { // Forward the error to the error callback. if (onError) call(onError, thrown); } // Continue iteration. return _runSequence(sequence, stopped, onNext, onError); }