@thi.ng/fibers 0.6.3 → 0.6.5
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 +1 -1
- package/README.md +1 -1
- package/api.js +18 -19
- package/csp.js +200 -279
- package/fiber.js +396 -393
- package/ops.js +137 -325
- package/package.json +14 -12
package/fiber.js
CHANGED
|
@@ -1,406 +1,409 @@
|
|
|
1
|
-
var
|
|
2
|
-
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
4
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
5
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
6
|
+
if (decorator = decorators[i])
|
|
7
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
8
|
+
if (kind && result)
|
|
9
|
+
__defProp(target, key, result);
|
|
10
|
+
return result;
|
|
11
|
+
};
|
|
3
12
|
import { INotifyMixin } from "@thi.ng/api/mixins/inotify";
|
|
4
13
|
import { isFunction } from "@thi.ng/checks/is-function";
|
|
5
14
|
import { illegalArgs } from "@thi.ng/errors/illegal-arguments";
|
|
6
15
|
import { illegalState } from "@thi.ng/errors/illegal-state";
|
|
7
16
|
import { monotonic, prefixed } from "@thi.ng/idgen";
|
|
8
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
EVENT_FIBER_CANCELED,
|
|
19
|
+
EVENT_FIBER_DONE,
|
|
20
|
+
EVENT_FIBER_ERROR,
|
|
21
|
+
STATE_ACTIVE,
|
|
22
|
+
STATE_CANCELED,
|
|
23
|
+
STATE_DONE,
|
|
24
|
+
STATE_ERROR,
|
|
25
|
+
STATE_NEW
|
|
26
|
+
} from "./api.js";
|
|
9
27
|
let DEFAULT_ID_GEN = prefixed("fib-", monotonic());
|
|
10
|
-
|
|
11
|
-
const NO_RESULT = { done: false, value:
|
|
12
|
-
let Fiber =
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
else {
|
|
49
|
-
this.idgen = DEFAULT_ID_GEN;
|
|
50
|
-
}
|
|
51
|
-
if (this.idgen)
|
|
52
|
-
this.id = this.idgen.next();
|
|
53
|
-
this.gen = isFunction(gen) ? gen(this) : gen;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Co-routine which blocks whilst this fiber (incl. its children) is active.
|
|
57
|
-
* Then return this fiber's value.
|
|
58
|
-
*/
|
|
59
|
-
*[Symbol.iterator]() {
|
|
60
|
-
while (this.state <= STATE_ACTIVE && this.next() <= STATE_ACTIVE)
|
|
61
|
-
yield;
|
|
62
|
-
return this.value;
|
|
28
|
+
const setDefaultIDGen = (gen) => DEFAULT_ID_GEN = gen;
|
|
29
|
+
const NO_RESULT = { done: false, value: void 0 };
|
|
30
|
+
let Fiber = class {
|
|
31
|
+
/**
|
|
32
|
+
* This fiber's user provided or generated ID.
|
|
33
|
+
*/
|
|
34
|
+
id;
|
|
35
|
+
/**
|
|
36
|
+
* This fiber's parent.
|
|
37
|
+
*/
|
|
38
|
+
parent;
|
|
39
|
+
gen;
|
|
40
|
+
idgen;
|
|
41
|
+
state = STATE_NEW;
|
|
42
|
+
autoTerminate = false;
|
|
43
|
+
children = [];
|
|
44
|
+
value = void 0;
|
|
45
|
+
error;
|
|
46
|
+
logger;
|
|
47
|
+
user;
|
|
48
|
+
_promise;
|
|
49
|
+
constructor(gen, opts) {
|
|
50
|
+
if (opts) {
|
|
51
|
+
this.autoTerminate = !!opts.terminate;
|
|
52
|
+
this.logger = opts.logger;
|
|
53
|
+
this.parent = opts.parent;
|
|
54
|
+
this.user = {
|
|
55
|
+
init: opts.init,
|
|
56
|
+
deinit: opts.deinit,
|
|
57
|
+
catch: opts.catch
|
|
58
|
+
};
|
|
59
|
+
if (opts.id) {
|
|
60
|
+
this.id = opts.id;
|
|
61
|
+
} else {
|
|
62
|
+
this.idgen = opts.idgen || DEFAULT_ID_GEN;
|
|
63
|
+
}
|
|
64
|
+
} else {
|
|
65
|
+
this.idgen = DEFAULT_ID_GEN;
|
|
63
66
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
return this.value;
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* Returns true if this fiber is still in new or active state (i.e. still
|
|
102
|
-
* can be processed).
|
|
103
|
-
*/
|
|
104
|
-
isActive() {
|
|
105
|
-
return this.state <= STATE_ACTIVE;
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Returns child fiber for given `id`.
|
|
109
|
-
*
|
|
110
|
-
* @param id
|
|
111
|
-
*/
|
|
112
|
-
childForID(id) {
|
|
113
|
-
return this.children.find((f) => f.id === id);
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* Adds given `body` as child process to this fiber. If not already a
|
|
117
|
-
* {@link Fiber} instance, it will be wrapped as such incl. with given
|
|
118
|
-
* options. `opts` are only used for this latter case and will inherit this
|
|
119
|
-
* (parent) fiber's {@link FiberOpts.logger} and {@link FiberOpts.idgen} as
|
|
120
|
-
* defaults. Returns child fiber.
|
|
121
|
-
*
|
|
122
|
-
* @remarks
|
|
123
|
-
* Child fibers are only processed when the parent is processed (e.g. via
|
|
124
|
-
* {@link Fiber.run} or via `yield* fiber`). Also see {@link Fiber.join} to
|
|
125
|
-
* wait for all child processes to finish.
|
|
126
|
-
*
|
|
127
|
-
* Non-active child process (i.e. finished, cancelled or errored) are
|
|
128
|
-
* automatically removed from the parent. If the child fiber is needed for
|
|
129
|
-
* future inspection, the return value of `fork()` should be stored by the
|
|
130
|
-
* user. Whilst still active, child fibers can also be looked up via
|
|
131
|
-
* {@link Fiber.childForID}.
|
|
132
|
-
*
|
|
133
|
-
* @example
|
|
134
|
-
* ```ts
|
|
135
|
-
* fiber(function* (ctx) {
|
|
136
|
-
* console.log("main start")
|
|
137
|
-
* // create 2 child processes
|
|
138
|
-
* ctx.fork(function* () {
|
|
139
|
-
* console.log("child1 start");
|
|
140
|
-
* yield* wait(500);
|
|
141
|
-
* console.log("child1 end");
|
|
142
|
-
* });
|
|
143
|
-
* // second process will take longer than first
|
|
144
|
-
* ctx.fork(function* () {
|
|
145
|
-
* console.log("child2 start");
|
|
146
|
-
* yield* wait(1000);
|
|
147
|
-
* console.log("child2 end");
|
|
148
|
-
* });
|
|
149
|
-
* // wait for children to complete
|
|
150
|
-
* yield* ctx.join();
|
|
151
|
-
* console.log("main end")
|
|
152
|
-
* }).run();
|
|
153
|
-
*
|
|
154
|
-
* // main start
|
|
155
|
-
* // child1 start
|
|
156
|
-
* // child2 start
|
|
157
|
-
* // child1 end
|
|
158
|
-
* // child2 end
|
|
159
|
-
* // main end
|
|
160
|
-
* ```
|
|
161
|
-
*
|
|
162
|
-
* @param f
|
|
163
|
-
* @param opts
|
|
164
|
-
*/
|
|
165
|
-
fork(body, opts) {
|
|
166
|
-
if (!this.isActive())
|
|
167
|
-
illegalState(`fiber (id: ${this.id}) not active`);
|
|
168
|
-
let $fiber;
|
|
169
|
-
if (body instanceof Fiber_1) {
|
|
170
|
-
if (body.parent)
|
|
171
|
-
illegalArgs(`fiber id: ${body.id} already has a parent`);
|
|
172
|
-
$fiber = body;
|
|
173
|
-
}
|
|
174
|
-
else {
|
|
175
|
-
$fiber = fiber(body, {
|
|
176
|
-
parent: this,
|
|
177
|
-
logger: this.logger,
|
|
178
|
-
idgen: this.idgen,
|
|
179
|
-
...opts,
|
|
180
|
-
});
|
|
67
|
+
if (this.idgen)
|
|
68
|
+
this.id = this.idgen.next();
|
|
69
|
+
this.gen = isFunction(gen) ? gen(this) : gen;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Co-routine which blocks whilst this fiber (incl. its children) is active.
|
|
73
|
+
* Then return this fiber's value.
|
|
74
|
+
*/
|
|
75
|
+
*[Symbol.iterator]() {
|
|
76
|
+
while (this.state <= STATE_ACTIVE && this.next() <= STATE_ACTIVE)
|
|
77
|
+
yield;
|
|
78
|
+
return this.value;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Returns a promise which only resolves when the fiber is not active
|
|
82
|
+
* anymore. If there was an unhandled error during the fiber execution the
|
|
83
|
+
* promise will reject, else if the fiber (incl. children) completed on its
|
|
84
|
+
* own or was cancelled, the promise resolves with the fiber's final
|
|
85
|
+
* (possibly `undefined`) value.
|
|
86
|
+
*
|
|
87
|
+
* @remarks
|
|
88
|
+
* The promise assumes the fiber either already has been (or will be)
|
|
89
|
+
* scheduled & executed via other means. This promise only repeatedly checks
|
|
90
|
+
* for any state changes of this fiber (at a configurable
|
|
91
|
+
* frequency/interval), but does *NOT* trigger fiber execution!
|
|
92
|
+
*
|
|
93
|
+
* @param delay
|
|
94
|
+
*/
|
|
95
|
+
promise(delay = 1) {
|
|
96
|
+
return this._promise || (this._promise = new Promise((resolve, reject) => {
|
|
97
|
+
const timerID = setInterval(() => {
|
|
98
|
+
if (this.state > STATE_ACTIVE) {
|
|
99
|
+
clearInterval(timerID);
|
|
100
|
+
this.state < STATE_ERROR ? resolve(this.value) : reject(this.error);
|
|
181
101
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
102
|
+
}, delay);
|
|
103
|
+
}));
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Returns this fiber's result value (if any). Only available if the fiber
|
|
107
|
+
* completed successfully and produced a value (either by returning a value
|
|
108
|
+
* from the fiber's generator or externally via {@link Fiber.done}).
|
|
109
|
+
*/
|
|
110
|
+
deref() {
|
|
111
|
+
return this.value;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Returns true if this fiber is still in new or active state (i.e. still
|
|
115
|
+
* can be processed).
|
|
116
|
+
*/
|
|
117
|
+
isActive() {
|
|
118
|
+
return this.state <= STATE_ACTIVE;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Returns child fiber for given `id`.
|
|
122
|
+
*
|
|
123
|
+
* @param id
|
|
124
|
+
*/
|
|
125
|
+
childForID(id) {
|
|
126
|
+
return this.children.find((f) => f.id === id);
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Adds given `body` as child process to this fiber. If not already a
|
|
130
|
+
* {@link Fiber} instance, it will be wrapped as such incl. with given
|
|
131
|
+
* options. `opts` are only used for this latter case and will inherit this
|
|
132
|
+
* (parent) fiber's {@link FiberOpts.logger} and {@link FiberOpts.idgen} as
|
|
133
|
+
* defaults. Returns child fiber.
|
|
134
|
+
*
|
|
135
|
+
* @remarks
|
|
136
|
+
* Child fibers are only processed when the parent is processed (e.g. via
|
|
137
|
+
* {@link Fiber.run} or via `yield* fiber`). Also see {@link Fiber.join} to
|
|
138
|
+
* wait for all child processes to finish.
|
|
139
|
+
*
|
|
140
|
+
* Non-active child process (i.e. finished, cancelled or errored) are
|
|
141
|
+
* automatically removed from the parent. If the child fiber is needed for
|
|
142
|
+
* future inspection, the return value of `fork()` should be stored by the
|
|
143
|
+
* user. Whilst still active, child fibers can also be looked up via
|
|
144
|
+
* {@link Fiber.childForID}.
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* ```ts
|
|
148
|
+
* fiber(function* (ctx) {
|
|
149
|
+
* console.log("main start")
|
|
150
|
+
* // create 2 child processes
|
|
151
|
+
* ctx.fork(function* () {
|
|
152
|
+
* console.log("child1 start");
|
|
153
|
+
* yield* wait(500);
|
|
154
|
+
* console.log("child1 end");
|
|
155
|
+
* });
|
|
156
|
+
* // second process will take longer than first
|
|
157
|
+
* ctx.fork(function* () {
|
|
158
|
+
* console.log("child2 start");
|
|
159
|
+
* yield* wait(1000);
|
|
160
|
+
* console.log("child2 end");
|
|
161
|
+
* });
|
|
162
|
+
* // wait for children to complete
|
|
163
|
+
* yield* ctx.join();
|
|
164
|
+
* console.log("main end")
|
|
165
|
+
* }).run();
|
|
166
|
+
*
|
|
167
|
+
* // main start
|
|
168
|
+
* // child1 start
|
|
169
|
+
* // child2 start
|
|
170
|
+
* // child1 end
|
|
171
|
+
* // child2 end
|
|
172
|
+
* // main end
|
|
173
|
+
* ```
|
|
174
|
+
*
|
|
175
|
+
* @param f
|
|
176
|
+
* @param opts
|
|
177
|
+
*/
|
|
178
|
+
fork(body, opts) {
|
|
179
|
+
if (!this.isActive())
|
|
180
|
+
illegalState(`fiber (id: ${this.id}) not active`);
|
|
181
|
+
let $fiber;
|
|
182
|
+
if (body instanceof Fiber) {
|
|
183
|
+
if (body.parent)
|
|
184
|
+
illegalArgs(`fiber id: ${body.id} already has a parent`);
|
|
185
|
+
$fiber = body;
|
|
186
|
+
} else {
|
|
187
|
+
$fiber = fiber(body, {
|
|
188
|
+
parent: this,
|
|
189
|
+
logger: this.logger,
|
|
190
|
+
idgen: this.idgen,
|
|
191
|
+
...opts
|
|
192
|
+
});
|
|
185
193
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
this.catch(e);
|
|
252
|
-
}
|
|
253
|
-
break;
|
|
254
|
-
default:
|
|
194
|
+
this.children.push($fiber);
|
|
195
|
+
this.logger?.debug("forking", $fiber.id);
|
|
196
|
+
return $fiber;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Calls {@link Fiber.fork} for all given fibers and returns them as array.
|
|
200
|
+
*
|
|
201
|
+
* @remarks
|
|
202
|
+
* Also see {@link Fiber.join} to wait for all child processes to complete.
|
|
203
|
+
*
|
|
204
|
+
* @param fibers
|
|
205
|
+
*/
|
|
206
|
+
forkAll(...fibers) {
|
|
207
|
+
return fibers.map((f) => this.fork(f));
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Waits for all child processes to complete/terminate. Use as `yield*
|
|
211
|
+
* fiber.join()`.
|
|
212
|
+
*
|
|
213
|
+
* @remarks
|
|
214
|
+
* See {@link Fiber.fork}, {@link Fiber.forkAll}.
|
|
215
|
+
*
|
|
216
|
+
*/
|
|
217
|
+
*join() {
|
|
218
|
+
this.logger?.debug("waiting for children...");
|
|
219
|
+
while (this.children.length)
|
|
220
|
+
yield;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Processes a single iteration of this fiber and any of its children. Does
|
|
224
|
+
* nothing if the fiber is not active anymore. Returns fiber's state.
|
|
225
|
+
*
|
|
226
|
+
* @remarks
|
|
227
|
+
* New, ininitialized fibers are first initialized via {@link Fiber.init}.
|
|
228
|
+
* Likewise, when fibers are terminated (for whatever reason), they will be
|
|
229
|
+
* de-initialized via {@link Fiber.deinit}. For all of these cases
|
|
230
|
+
* (init/deinit), hooks for user customization are provided via
|
|
231
|
+
* {@link FiberOpts.init}, {@link FiberOpts.deinit} and
|
|
232
|
+
* {@link FiberOpts.catch}.
|
|
233
|
+
*/
|
|
234
|
+
next() {
|
|
235
|
+
switch (this.state) {
|
|
236
|
+
case STATE_NEW:
|
|
237
|
+
this.init();
|
|
238
|
+
case STATE_ACTIVE:
|
|
239
|
+
try {
|
|
240
|
+
const { children } = this;
|
|
241
|
+
if (children.length) {
|
|
242
|
+
for (let i = 0, n = children.length; i < n; ) {
|
|
243
|
+
const child = children[i];
|
|
244
|
+
if (child.state > STATE_ACTIVE || child.next() > STATE_ACTIVE) {
|
|
245
|
+
children.splice(i, 1);
|
|
246
|
+
n--;
|
|
247
|
+
} else
|
|
248
|
+
i++;
|
|
249
|
+
}
|
|
250
|
+
} else if (this.autoTerminate) {
|
|
251
|
+
this.cancel();
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
const res = this.gen ? this.gen.next(this) : NO_RESULT;
|
|
255
|
+
if (res.done)
|
|
256
|
+
this.done(res.value);
|
|
257
|
+
} catch (e) {
|
|
258
|
+
this.catch(e);
|
|
255
259
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
init() {
|
|
259
|
-
this.logger?.debug("init", this.id);
|
|
260
|
-
this.user?.init?.(this);
|
|
261
|
-
this.state = STATE_ACTIVE;
|
|
262
|
-
}
|
|
263
|
-
deinit() {
|
|
264
|
-
this.logger?.debug("deinit", this.id);
|
|
265
|
-
this.user?.deinit?.(this);
|
|
266
|
-
this.children.length = 0;
|
|
267
|
-
this.gen = null;
|
|
268
|
-
}
|
|
269
|
-
/**
|
|
270
|
-
* Cancels further processing of this fiber and its children (if any). Calls
|
|
271
|
-
* {@link Fiber.deinit} and emits {@link EVENT_FIBER_CANCELED} event.
|
|
272
|
-
*
|
|
273
|
-
* @remarks
|
|
274
|
-
* Function is a no-op if the fiber is not active anymore.
|
|
275
|
-
*/
|
|
276
|
-
cancel() {
|
|
277
|
-
if (!this.isActive())
|
|
278
|
-
return;
|
|
279
|
-
this.logger?.debug("cancel", this.id);
|
|
280
|
-
for (let child of this.children)
|
|
281
|
-
child.cancel();
|
|
282
|
-
this.deinit();
|
|
283
|
-
this.state = STATE_CANCELED;
|
|
284
|
-
this.idgen?.free(this.id);
|
|
285
|
-
this.notify({ id: EVENT_FIBER_CANCELED, target: this });
|
|
286
|
-
}
|
|
287
|
-
/**
|
|
288
|
-
* Stops further processing of this fiber and its children (if any) and sets
|
|
289
|
-
* this fiber's value to given `value`. Calls {@link Fiber.deinit} and emits
|
|
290
|
-
* {@link EVENT_FIBER_DONE} event.
|
|
291
|
-
*
|
|
292
|
-
* @remarks
|
|
293
|
-
* Function is a no-op if the fiber is not active anymore.
|
|
294
|
-
*
|
|
295
|
-
* @param value
|
|
296
|
-
*/
|
|
297
|
-
done(value) {
|
|
298
|
-
if (!this.isActive())
|
|
299
|
-
return;
|
|
300
|
-
this.logger?.debug("done", this.id, value);
|
|
301
|
-
this.value = value;
|
|
302
|
-
for (let child of this.children)
|
|
303
|
-
child.done();
|
|
304
|
-
this.deinit();
|
|
305
|
-
this.state = STATE_DONE;
|
|
306
|
-
this.idgen?.free(this.id);
|
|
307
|
-
this.notify({ id: EVENT_FIBER_DONE, target: this, value });
|
|
308
|
-
}
|
|
309
|
-
/**
|
|
310
|
-
* Stops further processing of this fiber, cancels all child processes (if
|
|
311
|
-
* any) and sets this fiber's {@link Fiber.error} value to given `error`.
|
|
312
|
-
* Calls {@link Fiber.deinit} and emits {@link EVENT_FIBER_ERROR} event.
|
|
313
|
-
*
|
|
314
|
-
* @remarks
|
|
315
|
-
* Function is a no-op if the fiber already is in an error state. See
|
|
316
|
-
* {@link FiberOpts.catch} for details about user provided error handling
|
|
317
|
-
* and interception logic.
|
|
318
|
-
*
|
|
319
|
-
* @param err
|
|
320
|
-
*/
|
|
321
|
-
catch(err) {
|
|
322
|
-
if (this.state >= STATE_ERROR || this.user?.catch?.(this, err))
|
|
323
|
-
return;
|
|
324
|
-
this.logger
|
|
325
|
-
? this.logger.severe(`error ${this.id}:`, err)
|
|
326
|
-
: console.warn(`error ${this.id}:`, err);
|
|
327
|
-
for (let child of this.children)
|
|
328
|
-
child.cancel();
|
|
329
|
-
this.state = STATE_ERROR;
|
|
330
|
-
this.error = err;
|
|
331
|
-
this.deinit();
|
|
332
|
-
this.idgen?.free(this.id);
|
|
333
|
-
this.notify({ id: EVENT_FIBER_ERROR, target: this, value: err });
|
|
334
|
-
}
|
|
335
|
-
// @ts-ignore mixin
|
|
336
|
-
// prettier-ignore
|
|
337
|
-
addListener(id, fn, scope) { }
|
|
338
|
-
// @ts-ignore mixin
|
|
339
|
-
// prettier-ignore
|
|
340
|
-
removeListener(id, fn, scope) { }
|
|
341
|
-
// @ts-ignore mixin
|
|
342
|
-
notify(event) { }
|
|
343
|
-
/**
|
|
344
|
-
* Calls {@link Fiber.runWith} using default loop handlers.
|
|
345
|
-
*
|
|
346
|
-
* @remarks
|
|
347
|
-
* Current default handlers:
|
|
348
|
-
*
|
|
349
|
-
* - `requestAnimationFrame()` in browsers
|
|
350
|
-
* - `setImmediate()` in NodeJs
|
|
351
|
-
* - `setTimeout(fn, 1)` otherwise
|
|
352
|
-
*/
|
|
353
|
-
run() {
|
|
354
|
-
return this.runWith(typeof requestAnimationFrame === "function"
|
|
355
|
-
? requestAnimationFrame
|
|
356
|
-
: typeof setImmediate === "function"
|
|
357
|
-
? setImmediate
|
|
358
|
-
: (fn) => setTimeout(fn, 1));
|
|
359
|
-
}
|
|
360
|
-
/**
|
|
361
|
-
* Starts fiber execution using the provided higher-order loop/interval
|
|
362
|
-
* `handler` (e.g. see {@link Fiber.run}).
|
|
363
|
-
*
|
|
364
|
-
* @remarks
|
|
365
|
-
* That given `handler` is used to repeatedly schedule the next execution of
|
|
366
|
-
* {@link Fiber.next} (indirectly, via a zero-arg helper function passed to
|
|
367
|
-
* the `handler`).
|
|
368
|
-
*
|
|
369
|
-
* Note: **Do not use `setInterval` instead of `setTimeout`**. The given
|
|
370
|
-
* `handler` must only manage a single execution step, not multiple.
|
|
371
|
-
*
|
|
372
|
-
* @example
|
|
373
|
-
* ```ts
|
|
374
|
-
* // start with custom higher frequency handler
|
|
375
|
-
* fiber(function*() {
|
|
376
|
-
* while(true) {
|
|
377
|
-
* console.log("hello");
|
|
378
|
-
* yield;
|
|
379
|
-
* }
|
|
380
|
-
* }).runWith(setImmediate);
|
|
381
|
-
* ```
|
|
382
|
-
*
|
|
383
|
-
* @param handler
|
|
384
|
-
*/
|
|
385
|
-
runWith(handler) {
|
|
386
|
-
this.logger?.debug(`running ${this.id}...`);
|
|
387
|
-
const loop = () => {
|
|
388
|
-
if (this.state <= STATE_ACTIVE && this.next() <= STATE_ACTIVE)
|
|
389
|
-
handler(loop);
|
|
390
|
-
};
|
|
391
|
-
loop();
|
|
392
|
-
return this;
|
|
260
|
+
break;
|
|
261
|
+
default:
|
|
393
262
|
}
|
|
263
|
+
return this.state;
|
|
264
|
+
}
|
|
265
|
+
init() {
|
|
266
|
+
this.logger?.debug("init", this.id);
|
|
267
|
+
this.user?.init?.(this);
|
|
268
|
+
this.state = STATE_ACTIVE;
|
|
269
|
+
}
|
|
270
|
+
deinit() {
|
|
271
|
+
this.logger?.debug("deinit", this.id);
|
|
272
|
+
this.user?.deinit?.(this);
|
|
273
|
+
this.children.length = 0;
|
|
274
|
+
this.gen = null;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Cancels further processing of this fiber and its children (if any). Calls
|
|
278
|
+
* {@link Fiber.deinit} and emits {@link EVENT_FIBER_CANCELED} event.
|
|
279
|
+
*
|
|
280
|
+
* @remarks
|
|
281
|
+
* Function is a no-op if the fiber is not active anymore.
|
|
282
|
+
*/
|
|
283
|
+
cancel() {
|
|
284
|
+
if (!this.isActive())
|
|
285
|
+
return;
|
|
286
|
+
this.logger?.debug("cancel", this.id);
|
|
287
|
+
for (let child of this.children)
|
|
288
|
+
child.cancel();
|
|
289
|
+
this.deinit();
|
|
290
|
+
this.state = STATE_CANCELED;
|
|
291
|
+
this.idgen?.free(this.id);
|
|
292
|
+
this.notify({ id: EVENT_FIBER_CANCELED, target: this });
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Stops further processing of this fiber and its children (if any) and sets
|
|
296
|
+
* this fiber's value to given `value`. Calls {@link Fiber.deinit} and emits
|
|
297
|
+
* {@link EVENT_FIBER_DONE} event.
|
|
298
|
+
*
|
|
299
|
+
* @remarks
|
|
300
|
+
* Function is a no-op if the fiber is not active anymore.
|
|
301
|
+
*
|
|
302
|
+
* @param value
|
|
303
|
+
*/
|
|
304
|
+
done(value) {
|
|
305
|
+
if (!this.isActive())
|
|
306
|
+
return;
|
|
307
|
+
this.logger?.debug("done", this.id, value);
|
|
308
|
+
this.value = value;
|
|
309
|
+
for (let child of this.children)
|
|
310
|
+
child.done();
|
|
311
|
+
this.deinit();
|
|
312
|
+
this.state = STATE_DONE;
|
|
313
|
+
this.idgen?.free(this.id);
|
|
314
|
+
this.notify({ id: EVENT_FIBER_DONE, target: this, value });
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Stops further processing of this fiber, cancels all child processes (if
|
|
318
|
+
* any) and sets this fiber's {@link Fiber.error} value to given `error`.
|
|
319
|
+
* Calls {@link Fiber.deinit} and emits {@link EVENT_FIBER_ERROR} event.
|
|
320
|
+
*
|
|
321
|
+
* @remarks
|
|
322
|
+
* Function is a no-op if the fiber already is in an error state. See
|
|
323
|
+
* {@link FiberOpts.catch} for details about user provided error handling
|
|
324
|
+
* and interception logic.
|
|
325
|
+
*
|
|
326
|
+
* @param err
|
|
327
|
+
*/
|
|
328
|
+
catch(err) {
|
|
329
|
+
if (this.state >= STATE_ERROR || this.user?.catch?.(this, err))
|
|
330
|
+
return;
|
|
331
|
+
this.logger ? this.logger.severe(`error ${this.id}:`, err) : console.warn(`error ${this.id}:`, err);
|
|
332
|
+
for (let child of this.children)
|
|
333
|
+
child.cancel();
|
|
334
|
+
this.state = STATE_ERROR;
|
|
335
|
+
this.error = err;
|
|
336
|
+
this.deinit();
|
|
337
|
+
this.idgen?.free(this.id);
|
|
338
|
+
this.notify({ id: EVENT_FIBER_ERROR, target: this, value: err });
|
|
339
|
+
}
|
|
340
|
+
// @ts-ignore mixin
|
|
341
|
+
// prettier-ignore
|
|
342
|
+
addListener(id, fn, scope) {
|
|
343
|
+
}
|
|
344
|
+
// @ts-ignore mixin
|
|
345
|
+
// prettier-ignore
|
|
346
|
+
removeListener(id, fn, scope) {
|
|
347
|
+
}
|
|
348
|
+
// @ts-ignore mixin
|
|
349
|
+
notify(event) {
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Calls {@link Fiber.runWith} using default loop handlers.
|
|
353
|
+
*
|
|
354
|
+
* @remarks
|
|
355
|
+
* Current default handlers:
|
|
356
|
+
*
|
|
357
|
+
* - `requestAnimationFrame()` in browsers
|
|
358
|
+
* - `setImmediate()` in NodeJs
|
|
359
|
+
* - `setTimeout(fn, 1)` otherwise
|
|
360
|
+
*/
|
|
361
|
+
run() {
|
|
362
|
+
return this.runWith(
|
|
363
|
+
typeof requestAnimationFrame === "function" ? requestAnimationFrame : typeof setImmediate === "function" ? setImmediate : (fn) => setTimeout(fn, 1)
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Starts fiber execution using the provided higher-order loop/interval
|
|
368
|
+
* `handler` (e.g. see {@link Fiber.run}).
|
|
369
|
+
*
|
|
370
|
+
* @remarks
|
|
371
|
+
* That given `handler` is used to repeatedly schedule the next execution of
|
|
372
|
+
* {@link Fiber.next} (indirectly, via a zero-arg helper function passed to
|
|
373
|
+
* the `handler`).
|
|
374
|
+
*
|
|
375
|
+
* Note: **Do not use `setInterval` instead of `setTimeout`**. The given
|
|
376
|
+
* `handler` must only manage a single execution step, not multiple.
|
|
377
|
+
*
|
|
378
|
+
* @example
|
|
379
|
+
* ```ts
|
|
380
|
+
* // start with custom higher frequency handler
|
|
381
|
+
* fiber(function*() {
|
|
382
|
+
* while(true) {
|
|
383
|
+
* console.log("hello");
|
|
384
|
+
* yield;
|
|
385
|
+
* }
|
|
386
|
+
* }).runWith(setImmediate);
|
|
387
|
+
* ```
|
|
388
|
+
*
|
|
389
|
+
* @param handler
|
|
390
|
+
*/
|
|
391
|
+
runWith(handler) {
|
|
392
|
+
this.logger?.debug(`running ${this.id}...`);
|
|
393
|
+
const loop = () => {
|
|
394
|
+
if (this.state <= STATE_ACTIVE && this.next() <= STATE_ACTIVE)
|
|
395
|
+
handler(loop);
|
|
396
|
+
};
|
|
397
|
+
loop();
|
|
398
|
+
return this;
|
|
399
|
+
}
|
|
394
400
|
};
|
|
395
|
-
Fiber =
|
|
396
|
-
|
|
401
|
+
Fiber = __decorateClass([
|
|
402
|
+
INotifyMixin
|
|
397
403
|
], Fiber);
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
* @param opts
|
|
405
|
-
*/
|
|
406
|
-
export const fiber = (fiber, opts) => (fiber != null && fiber instanceof Fiber ? fiber : new Fiber(fiber, opts));
|
|
404
|
+
const fiber = (fiber2, opts) => fiber2 != null && fiber2 instanceof Fiber ? fiber2 : new Fiber(fiber2, opts);
|
|
405
|
+
export {
|
|
406
|
+
Fiber,
|
|
407
|
+
fiber,
|
|
408
|
+
setDefaultIDGen
|
|
409
|
+
};
|