Skip to content

Latest commit

 

History

History

fibers

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

@thi.ng/fibers

npm version npm downloads Mastodon Follow

This project is part of the @thi.ng/umbrella monorepo.

About

Process hierarchies & operators for cooperative multitasking.

This package provides a Fiber primitive acting as wrapper around ES6 generators (co-routines) and supplements them with nested child processes, cancellation, error & event handling and (optional) logging features. Additionally, there're a number of fiber-related utilities and higher order operators to construct and compose fibers.

Basic usage

import { fiber, wait } from "@thi.ng/fibers";
import { ConsoleLogger } from "@thi.ng/logger";

// wrap an endless generator as fiber
const app = fiber(
    function* () {
        while(true) {
            console.log("hello");
            // wait 0.25s
            yield* wait(250);
            console.log("fiber");
            // wait 1s
            yield* wait(1000);
        }
    }
);

// start processing it with default handlers
// see: https://docs.thi.ng/umbrella/fibers/classes/Fiber.html#run
app.run();

// create a child process which runs semi-independently
// (only executes if parent is still active)
// child fibers are auto-removed from parent when they terminate
const child = app.fork(
    // the `ctx` arg is the fiber wrapping this generator
    function* (ctx) {
        for(let i = 0; i < 3; i++) {
            ctx.logger?.debug("count", i);
            yield* wait(100);
        }
        // return value will be stored in fiber for future reference
        return 42;
    },
    // fiber options
    {
        // custom fiber ID (else autogenerated)
        id: "child-demo",
        // custom logger (default: none)
        logger: new ConsoleLogger("child")
    }
);

// hello
// [DEBUG] child: init child-demo
// [DEBUG] child: count 0
// [DEBUG] child: count 1
// [DEBUG] child: count 2
// fiber
// [DEBUG] child: done child-demo 42
// [DEBUG] child: deinit child-demo
// hello
// fiber
// hello
// ...

// once a fiber has completed, its value can be obtained
// e.g. here we create another fiber, which first waits for `child` to complete
app.fork(function* () {
    // wait for other fiber
    const result = yield* child;
    console.log("result", result);
    // alt way to obtain value
    console.log("deref", child.deref());
});
// result 42
// deref 42

Fiber operators

The following operators act as basic composition helpers to construct more elaborate fiber setups:

  • all: wait for all given fibers to complete
  • first: wait for one of the given fibers to complete
  • fork: create & attach a new child process
  • forkAll: create & attach multiple child processes
  • join: wait for all child processes to complete
  • sequence: execute fibers in sequence
  • timeSlice: execute fiber in batches of N milliseconds
  • until: wait until predicate is truthy
  • untilEvent: wait until event occurs
  • untilPromise: wait until promise resolves/rejects
  • untilState: stateful version of until
  • wait: wait for N milliseconds or indefinitely
  • waitFrames: wait for N frames/ticks
  • withTimeout: wait for given fiber, but only max N milliseconds

Composition via transducers

The @thi.ng/transducers package can be very helpful to create complex fiber setups, for example:

import { sequence, wait, type MaybeFiber } from "@thi.ng/fibers";
import {
    cycle,
    interpose,
    map,
    partition,
    repeatedly,
} from "@thi.ng/transducers";

const defWorkItem = (id: number) =>
    function* () {
        console.log("part", id);
    };

const defWorkGroup = (items: MaybeFiber[]) =>
    function* () {
        // interject a short pause between given work items
        // then execute in order and wait until all done
        yield* sequence(interpose(() => wait(100), items));
        console.log("---");
        yield* wait(1000);
    };

// create fiber which executes given sub-processes in order
sequence(
    // generate 25 work items
    // partition into groups of 5
    // transform into iterable of work groups
    // repeat indefinitely
    cycle(map(defWorkGroup, partition(5, repeatedly(defWorkItem, 25))))
).run();

// part 0
// part 1
// part 2
// part 3
// part 4
// ---
// part 5
// part 6
// part 7

Status

ALPHA - bleeding edge / work-in-progress

Search or submit any issues for this package

Installation

yarn add @thi.ng/fibers

ES module import:

<script type="module" src="https://cdn.skypack.dev/@thi.ng/fibers"></script>

Skypack documentation

For Node.js REPL:

const fibers = await import("@thi.ng/fibers");

Package sizes (brotli'd, pre-treeshake): ESM: 1.88 KB

Dependencies

Usage examples

Several demos in this repo's /examples directory are using this package.

A selection:

Screenshot Description Live demo Source
Fiber-based cooperative multitasking basics Demo Source

API

Generated API docs

TODO

Authors

If this project contributes to an academic publication, please cite it as:

@misc{thing-fibers,
  title = "@thi.ng/fibers",
  author = "Karsten Schmidt",
  note = "https://thi.ng/fibers",
  year = 2023
}

License

© 2023 Karsten Schmidt // Apache License 2.0