UNPKG

@thi.ng/fibers

Version:

Process hierarchies & operators for cooperative multitasking

232 lines 8.51 kB
import type { Event as $Event, Fn, Fn0, IDeref, IID, IIDGen, INotify, Listener, Maybe, Nullable } from "@thi.ng/api"; import type { ILogger } from "@thi.ng/logger"; import { type FiberEventType, type FiberFactory, type FiberOpts, type MaybeFiber, type State } from "./api.js"; export declare const setDefaultIDGen: (gen: IIDGen<string>) => IIDGen<string>; export declare class Fiber<T = any> implements IDeref<Maybe<T>>, IID<string>, INotify<FiberEventType> { /** * This fiber's user provided or generated ID. */ readonly id: string; /** * This fiber's parent. */ readonly parent?: Fiber; gen: Nullable<Generator<unknown, T>>; idgen?: IIDGen<string>; state: State; autoTerminate: boolean; children: Fiber[]; value?: T; error?: Error; logger?: ILogger; user?: Partial<Pick<FiberOpts, "init" | "deinit" | "catch">>; _promise?: Promise<Maybe<T>>; constructor(gen?: Nullable<FiberFactory<T> | Generator<unknown, T>>, opts?: Partial<FiberOpts>); /** * Co-routine which blocks whilst this fiber (incl. its children) is active. * Then return this fiber's value. */ [Symbol.iterator](): Generator<undefined, T | undefined, unknown>; /** * Returns a promise which only resolves when the fiber is not active * anymore. If there was an unhandled error during the fiber execution the * promise will reject, else if the fiber (incl. children) completed on its * own or was cancelled, the promise resolves with the fiber's final * (possibly `undefined`) value. * * @remarks * The promise assumes the fiber either already has been (or will be) * scheduled & executed via other means. This promise only repeatedly checks * for any state changes of this fiber (at a configurable * frequency/interval), but does *NOT* trigger fiber execution! * * @param delay */ promise(delay?: number): Promise<Maybe<T>>; /** * Returns this fiber's result value (if any). Only available if the fiber * completed successfully and produced a value (either by returning a value * from the fiber's generator or externally via {@link Fiber.done}). */ deref(): T | undefined; /** * Returns true if this fiber is still in new or active state (i.e. still * can be processed). */ isActive(): boolean; /** * Returns child fiber for given `id`. * * @param id */ childForID(id: string): Fiber<any> | undefined; /** * Adds given `body` as child process to this fiber. If not already a * {@link Fiber} instance, it will be wrapped as such incl. with given * options. `opts` are only used for this latter case and will inherit this * (parent) fiber's {@link FiberOpts.logger} and {@link FiberOpts.idgen} as * defaults. Returns child fiber. * * @remarks * Child fibers are only processed when the parent is processed (e.g. via * {@link Fiber.run} or via `yield* fiber`). Also see {@link Fiber.join} to * wait for all child processes to finish. * * Non-active child process (i.e. finished, cancelled or errored) are * automatically removed from the parent. If the child fiber is needed for * future inspection, the return value of `fork()` should be stored by the * user. Whilst still active, child fibers can also be looked up via * {@link Fiber.childForID}. * * @example * ```ts tangle:../export/fork.ts * import { fiber, wait } from "@thi.ng/fibers"; * * fiber(function* (ctx) { * console.log("main start") * // create 2 child processes * ctx.fork(function* () { * console.log("child1 start"); * yield* wait(500); * console.log("child1 end"); * }); * // second process will take longer than first * ctx.fork(function* () { * console.log("child2 start"); * yield* wait(1000); * console.log("child2 end"); * }); * // wait for children to complete * yield* ctx.join(); * console.log("main end") * }).run(); * * // main start * // child1 start * // child2 start * // child1 end * // child2 end * // main end * ``` * * @param body * @param opts */ fork<F>(body?: Nullable<MaybeFiber<F>>, opts?: Partial<FiberOpts>): Fiber<any>; /** * Calls {@link Fiber.fork} for all given fibers and returns them as array. * * @remarks * Also see {@link Fiber.join} to wait for all child processes to complete. * * @param fibers */ forkAll(...fibers: MaybeFiber[]): Fiber[]; /** * Waits for all child processes to complete/terminate. Use as `yield* * fiber.join()`. * * @remarks * See {@link Fiber.fork}, {@link Fiber.forkAll}. * */ join(): Generator<undefined, void, unknown>; /** * Processes a single iteration of this fiber and any of its children. Does * nothing if the fiber is not active anymore. Returns fiber's state. * * @remarks * New, ininitialized fibers are first initialized via {@link Fiber.init}. * Likewise, when fibers are terminated (for whatever reason), they will be * de-initialized via {@link Fiber.deinit}. For all of these cases * (init/deinit), hooks for user customization are provided via * {@link FiberOpts.init}, {@link FiberOpts.deinit} and * {@link FiberOpts.catch}. */ next(): State; protected init(): void; protected deinit(): void; /** * Cancels further processing of this fiber and its children (if any). Calls * {@link Fiber.deinit} and emits {@link EVENT_FIBER_CANCELED} event. * * @remarks * Function is a no-op if the fiber is not active anymore. */ cancel(): void; /** * Stops further processing of this fiber and its children (if any) and sets * this fiber's value to given `value`. Calls {@link Fiber.deinit} and emits * {@link EVENT_FIBER_DONE} event. * * @remarks * Function is a no-op if the fiber is not active anymore. * * @param value */ done(value?: T): void; /** * Stops further processing of this fiber, cancels all child processes (if * any) and sets this fiber's {@link Fiber.error} value to given `error`. * Calls {@link Fiber.deinit} and emits {@link EVENT_FIBER_ERROR} event. * * @remarks * Function is a no-op if the fiber already is in an error state. See * {@link FiberOpts.catch} for details about user provided error handling * and interception logic. * * @param err */ catch(err: Error): void; addListener(id: FiberEventType, fn: Listener<FiberEventType>, scope?: any): boolean; removeListener(id: FiberEventType, fn: Listener<FiberEventType>, scope?: any): boolean; notify(event: $Event<FiberEventType>): boolean; /** * Calls {@link Fiber.runWith} using default loop handlers. * * @remarks * Current default handlers: * * - `requestAnimationFrame()` in browsers * - `setImmediate()` in NodeJs * - `setTimeout(fn, 1)` otherwise */ run(): this; /** * Starts fiber execution using the provided higher-order loop/interval * `handler` (e.g. see {@link Fiber.run}). * * @remarks * That given `handler` is used to repeatedly schedule the next execution of * {@link Fiber.next} (indirectly, via a zero-arg helper function passed to * the `handler`). * * Note: **Do not use `setInterval` instead of `setTimeout`**. The given * `handler` must only manage a single execution step, not multiple. * * @example * ```ts tangle:../export/run-with.ts * import { fiber } from "@thi.ng/fibers"; * * // start with custom higher frequency handler * fiber(function*() { * while(true) { * console.log("hello"); * yield; * } * }).runWith(setImmediate); * ``` * * @param handler */ runWith(handler: Fn<Fn0<void>, void>): this; } /** * Functional syntax sugar for {@link Fiber} constructor. The `opts` are only * used if given a generator or {@link FiberFactory}. * * @param fiber * @param opts */ export declare const fiber: <T>(fiber?: Nullable<MaybeFiber<T>>, opts?: Partial<FiberOpts>) => Fiber<T>; //# sourceMappingURL=fiber.d.ts.map