UNPKG

@thi.ng/fibers

Version:

Process hierarchies & operators for cooperative multitasking

145 lines (144 loc) 3.49 kB
import { shuffle as $shuffle } from "@thi.ng/arrays/shuffle"; import { now, timeDiff } from "@thi.ng/timestamp"; import { SYSTEM } from "@thi.ng/random/system"; import { STATE_ACTIVE, STATE_DONE, STATE_ERROR } from "./api.js"; import { Fiber, fiber } from "./fiber.js"; const wait = (delay) => delay !== void 0 ? untilPromise( new Promise((resolve) => setTimeout(resolve, delay)) ) : fiber(function* () { while (true) yield; }); function* waitFrames(delay) { while (delay-- > 0) yield; } const sequence = (fibers, opts) => fiber(function* (ctx) { for (let fiber2 of fibers) { const $fiber = ctx.fork(fiber2); while ($fiber.isActive()) yield; if ($fiber.state === STATE_ERROR) throw $fiber.error; ctx.value = $fiber.value; if ($fiber.state > STATE_DONE || ctx.state > STATE_ACTIVE) break; } return ctx.value; }, opts); const first = (fibers, opts) => fiber(function* (ctx) { const $fibers = [...fibers].map((f) => ctx.fork(f)); while (true) { for (let f of $fibers) { if (!f.isActive()) return f; } yield; } }, opts); const all = (fibers, opts) => fiber((ctx) => { ctx.forkAll(...fibers); return ctx.join(); }, opts); const withTimeout = (body, timeout, opts) => first([body, wait(timeout)], opts); const timeSlice = (body, maxTime, opts) => fiber(function* () { const $fiber = fiber(body); while (true) { let t0 = now(); do { if ($fiber.state > STATE_ACTIVE || $fiber.next() > STATE_ACTIVE) return; } while (timeDiff(t0) < maxTime); yield; } }, opts); const timeSliceIterable = (src, consume, maxTime, opts) => fiber(function* () { const iter = src[Symbol.iterator](); while (true) { let t0 = now(); const buf = []; do { const { value, done } = iter.next(); if (done) { consume(buf); return; } buf.push(value); } while (timeDiff(t0) < maxTime); consume(buf); yield; } }, opts); function* until(pred) { while (!pred()) yield; } function* untilState(state, pred) { while (!pred(state)) yield; } const untilPromise = (promise, opts) => fiber(function* (ctx) { let error; promise.then( (x) => ctx.done(x), (e) => error = e ); while (true) { if (error) throw error; yield; } }, opts); const untilEvent = (target, type, opts) => { let listener; return fiber(null, { ...opts, init(ctx) { listener = (e) => ctx.done(e); target.addEventListener(type, listener); }, deinit() { target.removeEventListener(type, listener); } }); }; class Shuffle extends Fiber { rnd; constructor(fibers, opts) { super((ctx) => ctx.join(), opts); this.rnd = opts?.rnd || SYSTEM; this.forkAll(...fibers); } next() { if (!this.isActive()) return this.state; $shuffle(this.children, this.children.length, this.rnd); return super.next(); } } const shuffle = (fibers, opts) => new Shuffle(fibers, opts); const asPromise = (task, opts) => new Promise((resolve, reject) => { fiber(task, { ...opts, deinit: (ctx) => { opts?.deinit?.(ctx); ctx.state < STATE_ERROR && resolve(ctx.deref()); }, catch: (ctx, e) => { if (opts?.catch?.(ctx, e)) return true; reject(e); return false; } }).run(); }); export { Shuffle, all, asPromise, first, sequence, shuffle, timeSlice, timeSliceIterable, until, untilEvent, untilPromise, untilState, wait, waitFrames, withTimeout };