@thi.ng/fibers 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/ops.js ADDED
@@ -0,0 +1,198 @@
1
+ import { now, timeDiff } from "@thi.ng/bench/now";
2
+ import { STATE_ACTIVE, STATE_DONE, STATE_ERROR, } from "./api.js";
3
+ import { Fiber, fiber } from "./fiber.js";
4
+ /**
5
+ * Returns co-routine which "blocks" for given number of milliseconds or
6
+ * indefinitely.
7
+ *
8
+ * @param delay
9
+ */
10
+ export const wait = (delay) => delay !== undefined
11
+ ? untilPromise(new Promise((resolve) => setTimeout(resolve, delay)))
12
+ : fiber(function* () {
13
+ while (true)
14
+ yield;
15
+ });
16
+ /**
17
+ * Returns ES6 generator which "blocks" for given number of frames.
18
+ *
19
+ * @param delay
20
+ */
21
+ export function* waitFrames(delay) {
22
+ while (delay-- > 0)
23
+ yield;
24
+ }
25
+ /**
26
+ * Returns a fiber which executes given fibers in sequence until all are
27
+ * complete or one of them errored or got canceled.
28
+ *
29
+ * @param fibers
30
+ * @param opts
31
+ */
32
+ export const sequence = (fibers, opts) => fiber(function* (ctx) {
33
+ for (let fiber of fibers) {
34
+ const $fiber = ctx.fork(fiber);
35
+ while ($fiber.isActive())
36
+ yield;
37
+ if ($fiber.state === STATE_ERROR)
38
+ throw $fiber.error;
39
+ ctx.value = $fiber.value;
40
+ if ($fiber.state > STATE_DONE || ctx.state > STATE_ACTIVE)
41
+ break;
42
+ }
43
+ return ctx.value;
44
+ }, opts);
45
+ /**
46
+ * Returns a fiber which executes given fibers as child processes until **one**
47
+ * of them is finished/terminated. That child fiber itself will be the result.
48
+ *
49
+ * @remarks
50
+ * Also see {@link withTimeout}, {@link all}.
51
+ *
52
+ * @example
53
+ * ```ta
54
+ * // wait until mouse click for max 5 seconds
55
+ * const res = yield* first([
56
+ * untilEvent(window, "click", { id: "click" }),
57
+ * wait(5000)
58
+ * ]);
59
+ *
60
+ * // one way to check result
61
+ * if (res.id === "click") { ... }
62
+ * ```
63
+ *
64
+ * @param fibers
65
+ * @param opts
66
+ */
67
+ export const first = (fibers, opts) => fiber(function* (ctx) {
68
+ const $fibers = fibers.map((f) => ctx.fork(f));
69
+ while (true) {
70
+ for (let f of $fibers) {
71
+ if (!f.isActive())
72
+ return f;
73
+ }
74
+ yield;
75
+ }
76
+ }, opts);
77
+ /**
78
+ * Returns a fiber which executes given fibers as child processes until **all**
79
+ * of them are finished/terminated.
80
+ *
81
+ * @remarks
82
+ * Also see {@link first}.
83
+ *
84
+ * @param fibers
85
+ * @param opts
86
+ */
87
+ export const all = (fibers, opts) => fiber((ctx) => {
88
+ for (let f of fibers)
89
+ ctx.fork(f);
90
+ return ctx.join();
91
+ }, opts);
92
+ /**
93
+ * Syntax sugar common use cases of {@link first} where a child fiber should be
94
+ * limited to a max. time period before giving up.
95
+ *
96
+ * @example
97
+ * ```ts
98
+ * // wait for fetch response max. 5 seconds
99
+ * const res = yield* withTimeout(untilPromise(fetch("example.json")), 5000);
100
+ *
101
+ * if (res.deref() != null) { ... }
102
+ * ```
103
+ *
104
+ * @param fiber
105
+ * @param timeout
106
+ * @param opts
107
+ */
108
+ export const withTimeout = (fiber, timeout, opts) => first([fiber, wait(timeout)], opts);
109
+ /**
110
+ * Higher-order fiber which repeatedly executes given `fiber` until its
111
+ * completion, but does so in a time-sliced manner, such that the fiber never
112
+ * consumes more than `maxTime` milliseconds per update cycle.
113
+ *
114
+ * @param fiber
115
+ * @param maxTime
116
+ * @param opts
117
+ */
118
+ export const timeSlice = (body, maxTime, opts) => fiber(function* () {
119
+ const $fiber = fiber(body);
120
+ while (true) {
121
+ let t0 = now();
122
+ do {
123
+ if ($fiber.state > STATE_ACTIVE || $fiber.next() > STATE_ACTIVE)
124
+ return;
125
+ } while (timeDiff(t0, now()) < maxTime);
126
+ yield;
127
+ }
128
+ }, opts);
129
+ /**
130
+ * Returns a fiber which "blocks" until given predicate function returns true.
131
+ *
132
+ * @remarks
133
+ * See {@link untilState} for stateful version.
134
+ *
135
+ * @param pred
136
+ */
137
+ export function* until(pred) {
138
+ while (!pred())
139
+ yield;
140
+ }
141
+ /**
142
+ * Stateful version of {@link until}. Takes an arbitrary `state`
143
+ * value/container and returns a fiber which "blocks" until given predicate
144
+ * function returns true. The `state` is passed to the predicate in each
145
+ * iteration.
146
+ *
147
+ * @param state
148
+ * @param pred
149
+ */
150
+ export function* untilState(state, pred) {
151
+ while (!pred(state))
152
+ yield;
153
+ }
154
+ /**
155
+ * Returns a fiber which "blocks" until the given `promise` resolves or rejects.
156
+ * In the latter case, the fiber will throw the received error.
157
+ *
158
+ * @remove
159
+ * If the erroring fiber was added directly to a {@link FiberPool}, the error
160
+ * will be logged and the fiber removed. See {@link FiberPool.update} for
161
+ * details.
162
+ *
163
+ * @param promise
164
+ */
165
+ export const untilPromise = (promise) => fiber(function* (ctx) {
166
+ let error;
167
+ promise.then((x) => ctx.done(x), (e) => (error = e));
168
+ while (true) {
169
+ if (error)
170
+ throw error;
171
+ yield;
172
+ }
173
+ });
174
+ /**
175
+ * Returns fiber which attaches a one-off event handler for event `type` to
176
+ * `target` and then "blocks" until the event occurred.
177
+ *
178
+ * @remarks
179
+ * The event handler will be removed when the fiber terminates. Upon completion,
180
+ * the event will be the fiber's {@link Fiber.value}.
181
+ *
182
+ * @param target
183
+ * @param type
184
+ * @param opts
185
+ */
186
+ export const untilEvent = (target, type, opts) => {
187
+ let listener;
188
+ return fiber(null, {
189
+ ...opts,
190
+ init(ctx) {
191
+ listener = (e) => ctx.done(e);
192
+ target.addEventListener(type, listener);
193
+ },
194
+ deinit() {
195
+ target.removeEventListener(type, listener);
196
+ },
197
+ });
198
+ };
package/package.json ADDED
@@ -0,0 +1,86 @@
1
+ {
2
+ "name": "@thi.ng/fibers",
3
+ "version": "0.1.0",
4
+ "description": "Process hierarchies & operators for cooperative multitasking",
5
+ "type": "module",
6
+ "module": "./index.js",
7
+ "typings": "./index.d.ts",
8
+ "sideEffects": false,
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/thi-ng/umbrella.git"
12
+ },
13
+ "homepage": "https://github.com/thi-ng/umbrella/tree/develop/packages/fibers#readme",
14
+ "funding": [
15
+ {
16
+ "type": "github",
17
+ "url": "https://github.com/sponsors/postspectacular"
18
+ },
19
+ {
20
+ "type": "patreon",
21
+ "url": "https://patreon.com/thing_umbrella"
22
+ }
23
+ ],
24
+ "author": "Karsten Schmidt (https://thi.ng)",
25
+ "license": "Apache-2.0",
26
+ "scripts": {
27
+ "build": "yarn clean && tsc --declaration",
28
+ "clean": "rimraf --glob '*.js' '*.d.ts' '*.map' doc",
29
+ "doc": "typedoc --excludePrivate --excludeInternal --out doc src/index.ts",
30
+ "doc:ae": "mkdir -p .ae/doc .ae/temp && api-extractor run --local --verbose",
31
+ "doc:readme": "yarn doc:stats && tools:readme",
32
+ "doc:stats": "tools:module-stats",
33
+ "pub": "yarn npm publish --access public",
34
+ "test": "testament test"
35
+ },
36
+ "dependencies": {
37
+ "@thi.ng/api": "^8.9.0",
38
+ "@thi.ng/bench": "^3.4.1",
39
+ "@thi.ng/checks": "^3.4.0",
40
+ "@thi.ng/logger": "^1.4.16"
41
+ },
42
+ "devDependencies": {
43
+ "@microsoft/api-extractor": "^7.36.3",
44
+ "@thi.ng/testament": "^0.3.18",
45
+ "rimraf": "^5.0.1",
46
+ "tools": "^0.0.1",
47
+ "typedoc": "^0.24.8",
48
+ "typescript": "^5.1.6"
49
+ },
50
+ "keywords": [
51
+ "typescript"
52
+ ],
53
+ "publishConfig": {
54
+ "access": "public"
55
+ },
56
+ "browser": {
57
+ "process": false,
58
+ "setTimeout": false
59
+ },
60
+ "engines": {
61
+ "node": ">=14"
62
+ },
63
+ "files": [
64
+ "./*.js",
65
+ "./*.d.ts"
66
+ ],
67
+ "exports": {
68
+ ".": {
69
+ "default": "./index.js"
70
+ },
71
+ "./api": {
72
+ "default": "./api.js"
73
+ },
74
+ "./fiber": {
75
+ "default": "./fiber.js"
76
+ },
77
+ "./ops": {
78
+ "default": "./ops.js"
79
+ }
80
+ },
81
+ "thi.ng": {
82
+ "status": "alpha",
83
+ "year": 2023
84
+ },
85
+ "gitHead": "9fa3f7f8169efa30e3c71b43c82f77393581c3b5\n"
86
+ }