@thi.ng/fibers
Version:
Process hierarchies & operators for cooperative multitasking
232 lines • 8.51 kB
TypeScript
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