@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/CHANGELOG.md +22 -0
- package/LICENSE +201 -0
- package/README.md +246 -0
- package/api.d.ts +62 -0
- package/api.js +19 -0
- package/fiber.d.ts +204 -0
- package/fiber.js +347 -0
- package/index.d.ts +4 -0
- package/index.js +3 -0
- package/ops.d.ts +130 -0
- package/ops.js +198 -0
- package/package.json +86 -0
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
|
+
}
|