@fncts/io 0.0.30 → 0.0.31
Sign up to get free protection for your applications and to get access to all the features.
- package/IO/api/blocking.d.ts +7 -0
- package/IO.d.ts +1 -0
- package/Ref/Synchronized/definition.d.ts +3 -3
- package/Semaphore.d.ts +30 -0
- package/SubscriptionRef.d.ts +3 -3
- package/_cjs/Channel/api/mapOutConcurrentIO.cjs +14 -16
- package/_cjs/Channel/api/mapOutConcurrentIO.cjs.map +1 -1
- package/_cjs/Channel/api/mergeAllWith.cjs +30 -32
- package/_cjs/Channel/api/mergeAllWith.cjs.map +1 -1
- package/_cjs/IO/api/blocking.cjs +20 -0
- package/_cjs/IO/api/blocking.cjs.map +1 -0
- package/_cjs/IO.cjs +11 -0
- package/_cjs/IO.cjs.map +1 -1
- package/_cjs/Ref/Synchronized/constructors.cjs +4 -5
- package/_cjs/Ref/Synchronized/constructors.cjs.map +1 -1
- package/_cjs/Ref/Synchronized/definition.cjs +1 -2
- package/_cjs/Ref/Synchronized/definition.cjs.map +1 -1
- package/_cjs/Semaphore.cjs +90 -0
- package/_cjs/Semaphore.cjs.map +1 -0
- package/_cjs/Stream/api.cjs +47 -49
- package/_cjs/Stream/api.cjs.map +1 -1
- package/_cjs/SubscriptionRef.cjs +2 -3
- package/_cjs/SubscriptionRef.cjs.map +1 -1
- package/_cjs/internal/BackgroundScheduler.cjs +199 -0
- package/_cjs/internal/BackgroundScheduler.cjs.map +1 -0
- package/_mjs/Channel/api/mapOutConcurrentIO.mjs +14 -16
- package/_mjs/Channel/api/mapOutConcurrentIO.mjs.map +1 -1
- package/_mjs/Channel/api/mergeAllWith.mjs +30 -32
- package/_mjs/Channel/api/mergeAllWith.mjs.map +1 -1
- package/_mjs/IO/api/blocking.mjs +12 -0
- package/_mjs/IO/api/blocking.mjs.map +1 -0
- package/_mjs/IO.mjs +1 -0
- package/_mjs/IO.mjs.map +1 -1
- package/_mjs/Ref/Synchronized/constructors.mjs +4 -5
- package/_mjs/Ref/Synchronized/constructors.mjs.map +1 -1
- package/_mjs/Ref/Synchronized/definition.mjs +1 -2
- package/_mjs/Ref/Synchronized/definition.mjs.map +1 -1
- package/_mjs/Semaphore.mjs +78 -0
- package/_mjs/Semaphore.mjs.map +1 -0
- package/_mjs/Stream/api.mjs +47 -49
- package/_mjs/Stream/api.mjs.map +1 -1
- package/_mjs/SubscriptionRef.mjs +2 -3
- package/_mjs/SubscriptionRef.mjs.map +1 -1
- package/_mjs/internal/BackgroundScheduler.mjs +191 -0
- package/_mjs/internal/BackgroundScheduler.mjs.map +1 -0
- package/_src/Channel/api/mapOutConcurrentIO.ts +1 -1
- package/_src/Channel/api/mergeAllWith.ts +1 -1
- package/_src/IO/api/blocking.ts +9 -0
- package/_src/IO.ts +1 -0
- package/_src/Ref/Synchronized/constructors.ts +2 -2
- package/_src/Ref/Synchronized/definition.ts +1 -1
- package/_src/Semaphore.ts +81 -0
- package/_src/Stream/api.ts +1 -1
- package/_src/SubscriptionRef.ts +2 -2
- package/_src/global.ts +4 -0
- package/_src/internal/BackgroundScheduler.ts +276 -0
- package/global.d.ts +4 -0
- package/internal/BackgroundScheduler.d.ts +47 -0
- package/package.json +2 -2
@@ -35,7 +35,7 @@ export function mergeAllWith<OutDone>(
|
|
35
35
|
const cancelers = Δ(IO.acquireRelease(Queue.makeUnbounded<Future<never, void>>(), (queue) => queue.shutdown));
|
36
36
|
const lastDone = Δ(Ref.make<Maybe<OutDone>>(Nothing()));
|
37
37
|
const errorSignal = Δ(Future.make<never, void>());
|
38
|
-
const permits = Δ(
|
38
|
+
const permits = Δ(Semaphore(n));
|
39
39
|
const pull = Δ(channels.toPull);
|
40
40
|
const evaluatePull = (pull: IO<Env | Env1, OutErr | OutErr1, Either<OutDone, OutElem>>) =>
|
41
41
|
pull
|
@@ -0,0 +1,9 @@
|
|
1
|
+
import { backgroundScheduler } from "@fncts/io/internal/BackgroundScheduler";
|
2
|
+
|
3
|
+
/**
|
4
|
+
* @tsplus static fncts.io.IOOps blocking
|
5
|
+
* @tsplus getter fncts.io.IO blocking
|
6
|
+
*/
|
7
|
+
export function blocking<R, E, A>(self: IO<R, E, A>, __tsplusTrace?: string): IO<R, E, A> {
|
8
|
+
return FiberRef.currentScheduler.locally(backgroundScheduler)(IO.yieldNow) > self;
|
9
|
+
}
|
package/_src/IO.ts
CHANGED
@@ -13,6 +13,7 @@ export * from "./IO/api/addFinalizer.js";
|
|
13
13
|
export * from "./IO/api/addFinalizerExit.js";
|
14
14
|
export * from "./IO/api/asyncInterrupt.js";
|
15
15
|
export * from "./IO/api/asyncIO.js";
|
16
|
+
export * from "./IO/api/blocking.js";
|
16
17
|
export * from "./IO/api/bracket.js";
|
17
18
|
export * from "./IO/api/bracketExit.js";
|
18
19
|
export * from "./IO/api/clockWith.js";
|
@@ -5,7 +5,7 @@ import { PSynchronizedInternal } from "./definition.js";
|
|
5
5
|
*/
|
6
6
|
export function unsafeMakeSynchronized<A>(a: A, __tsplusTrace?: string): Ref.Synchronized<A> {
|
7
7
|
const ref = Ref.unsafeMake(a);
|
8
|
-
const semaphore =
|
8
|
+
const semaphore = Semaphore.unsafeMake(1);
|
9
9
|
return new PSynchronizedInternal(semaphore, ref.get, (a: A) => ref.set(a));
|
10
10
|
}
|
11
11
|
|
@@ -15,7 +15,7 @@ export function unsafeMakeSynchronized<A>(a: A, __tsplusTrace?: string): Ref.Syn
|
|
15
15
|
export function makeSynchronized<A>(a: Lazy<A>, __tsplusTrace?: string): UIO<Ref.Synchronized<A>> {
|
16
16
|
return Do((_) => {
|
17
17
|
const ref = _(Ref.make(a));
|
18
|
-
const semaphore = _(
|
18
|
+
const semaphore = _(Semaphore(1));
|
19
19
|
return new PSynchronizedInternal(semaphore, ref.get, (a: A) => ref.set(a));
|
20
20
|
});
|
21
21
|
}
|
@@ -30,7 +30,7 @@ export const Synchronized: PSynchronizedOps = {};
|
|
30
30
|
export class PSynchronizedInternal<RA, RB, EA, EB, A, B> extends RefInternal<RA, RB, EA, EB, A, B> {
|
31
31
|
readonly [SynchronizedTypeId]: SynchronizedTypeId = SynchronizedTypeId;
|
32
32
|
constructor(
|
33
|
-
readonly semaphore:
|
33
|
+
readonly semaphore: Semaphore,
|
34
34
|
readonly unsafeGet: IO<RB, EB, B>,
|
35
35
|
readonly unsafeSet: (a: A) => IO<RA, EA, void>,
|
36
36
|
) {
|
@@ -0,0 +1,81 @@
|
|
1
|
+
/**
|
2
|
+
* @tsplus type fncts.io.Semaphore
|
3
|
+
* @tsplus companion fncts.io.SemaphoreOps
|
4
|
+
*/
|
5
|
+
export class Semaphore {
|
6
|
+
constructor(readonly permits: number) {}
|
7
|
+
|
8
|
+
taken = 0;
|
9
|
+
waiters = new Set<() => void>();
|
10
|
+
|
11
|
+
get free(): number {
|
12
|
+
return this.permits - this.taken;
|
13
|
+
}
|
14
|
+
|
15
|
+
runNext() {
|
16
|
+
const next = this.waiters.values().next();
|
17
|
+
if (!next.done) {
|
18
|
+
this.waiters.delete(next.value);
|
19
|
+
next.value();
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
take(n: number) {
|
24
|
+
return IO.asyncInterrupt<never, never, number>((cb) => {
|
25
|
+
if (this.free < n) {
|
26
|
+
const observer = () => {
|
27
|
+
if (this.free >= n) {
|
28
|
+
this.waiters.delete(observer);
|
29
|
+
this.taken += n;
|
30
|
+
cb(IO.succeedNow(n));
|
31
|
+
}
|
32
|
+
};
|
33
|
+
this.waiters.add(observer);
|
34
|
+
return Either.left(
|
35
|
+
IO(() => {
|
36
|
+
this.waiters.delete(observer);
|
37
|
+
}),
|
38
|
+
);
|
39
|
+
}
|
40
|
+
this.taken += n;
|
41
|
+
return Either.right(IO.succeedNow(n));
|
42
|
+
});
|
43
|
+
}
|
44
|
+
|
45
|
+
release(n: number) {
|
46
|
+
return IO.withFiberRuntime<never, never, void>((fiber) => {
|
47
|
+
this.taken -= n;
|
48
|
+
fiber.getFiberRef(FiberRef.currentScheduler).scheduleTask(() => {
|
49
|
+
this.waiters.forEach((wake) => wake());
|
50
|
+
});
|
51
|
+
return IO.unit;
|
52
|
+
});
|
53
|
+
}
|
54
|
+
|
55
|
+
withPermits(permits: number) {
|
56
|
+
return <R, E, A>(io: IO<R, E, A>): IO<R, E, A> => {
|
57
|
+
return IO.uninterruptibleMask((restore) =>
|
58
|
+
restore(this.take(permits)).flatMap((permits) => restore(io).ensuring(this.release(permits))),
|
59
|
+
);
|
60
|
+
};
|
61
|
+
}
|
62
|
+
|
63
|
+
withPermit<R, E, A>(io: IO<R, E, A>): IO<R, E, A> {
|
64
|
+
return this.withPermits(1)(io);
|
65
|
+
}
|
66
|
+
}
|
67
|
+
|
68
|
+
/**
|
69
|
+
* @tsplus static fncts.io.SemaphoreOps unsafeMake
|
70
|
+
*/
|
71
|
+
export function unsafeMakeSemaphore(permits: number): Semaphore {
|
72
|
+
return new Semaphore(permits);
|
73
|
+
}
|
74
|
+
|
75
|
+
/**
|
76
|
+
* @tsplus static fncts.io.SemaphoreOps make
|
77
|
+
* @tsplus static fncts.io.SemaphoreOps __call
|
78
|
+
*/
|
79
|
+
export function makeSemaphore(permits: number, __tsplusTrace?: string): UIO<Semaphore> {
|
80
|
+
return IO(Semaphore.unsafeMake(permits));
|
81
|
+
}
|
package/_src/Stream/api.ts
CHANGED
@@ -1092,7 +1092,7 @@ export function distributedWithDynamic<E, A>(
|
|
1092
1092
|
);
|
1093
1093
|
const add = Δ(
|
1094
1094
|
Do((Δ) => {
|
1095
|
-
const queuesLock = Δ(
|
1095
|
+
const queuesLock = Δ(Semaphore(1));
|
1096
1096
|
const newQueue = Δ(
|
1097
1097
|
Ref.make<UIO<readonly [symbol, Queue<Exit<Maybe<E>, A>>]>>(
|
1098
1098
|
Do((Δ) => {
|
package/_src/SubscriptionRef.ts
CHANGED
@@ -6,7 +6,7 @@ export type SubscriptionRefTypeId = typeof SubscriptionRefTypeId;
|
|
6
6
|
|
7
7
|
export class SubscriptionRefInternal<A> extends PSynchronizedInternal<never, never, never, never, A, A> {
|
8
8
|
readonly [SubscriptionRefTypeId]: SubscriptionRefTypeId = SubscriptionRefTypeId;
|
9
|
-
constructor(readonly semaphore:
|
9
|
+
constructor(readonly semaphore: Semaphore, readonly hub: Hub<A>, readonly ref: Ref<A>) {
|
10
10
|
super(semaphore, ref.get, (a) => ref.set(a));
|
11
11
|
}
|
12
12
|
changes: Stream<never, never, A> = Stream.unwrapScoped(
|
@@ -50,7 +50,7 @@ export function concrete<A>(_: SubscriptionRef<A>): asserts _ is SubscriptionRef
|
|
50
50
|
*/
|
51
51
|
export function make<A>(value: Lazy<A>): UIO<SubscriptionRef<A>> {
|
52
52
|
return Do((Δ) => {
|
53
|
-
const semaphore = Δ(
|
53
|
+
const semaphore = Δ(Semaphore(1));
|
54
54
|
const hub = Δ(Hub.makeUnbounded<A>());
|
55
55
|
const ref = Δ(Ref.make(value));
|
56
56
|
return new SubscriptionRefInternal(semaphore, hub, ref);
|
package/_src/global.ts
CHANGED
@@ -119,6 +119,10 @@ import { Finalizer } from "@fncts/io/Scope/Finalizer";
|
|
119
119
|
* @tsplus global
|
120
120
|
*/
|
121
121
|
import { ScopedRef } from "@fncts/io/ScopedRef/definition";
|
122
|
+
/**
|
123
|
+
* @tsplus global
|
124
|
+
*/
|
125
|
+
import { Semaphore } from "@fncts/io/Semaphore";
|
122
126
|
/**
|
123
127
|
* @tsplus global
|
124
128
|
*/
|
@@ -0,0 +1,276 @@
|
|
1
|
+
import type { Scheduler } from "@fncts/io/internal/Scheduler";
|
2
|
+
|
3
|
+
interface IdleDeadline {
|
4
|
+
timeRemaining(): number;
|
5
|
+
readonly didTimeout: boolean;
|
6
|
+
}
|
7
|
+
|
8
|
+
declare const requestIdleCallback: ((callback: (deadline: IdleDeadline) => void) => number) | undefined;
|
9
|
+
declare const cancelIdleCallback: ((handle: number | undefined) => void) | undefined;
|
10
|
+
declare const requestAnimationFrame: ((callback: (time: number) => void) => void) | undefined;
|
11
|
+
interface Navigator {
|
12
|
+
scheduling:
|
13
|
+
| {
|
14
|
+
isInputPending: (() => boolean) | undefined;
|
15
|
+
}
|
16
|
+
| undefined;
|
17
|
+
}
|
18
|
+
declare const navigator: Navigator;
|
19
|
+
|
20
|
+
interface WhenReady<T> {
|
21
|
+
promise: () => Promise<T>;
|
22
|
+
resolve: (value: T) => void;
|
23
|
+
}
|
24
|
+
|
25
|
+
function whenReady<T>(): WhenReady<T> {
|
26
|
+
const observers: Array<(value: T) => void> = [];
|
27
|
+
|
28
|
+
const promise = () => new Promise<T>((resolve) => observers.push(resolve));
|
29
|
+
|
30
|
+
return {
|
31
|
+
promise,
|
32
|
+
resolve: (value) => observers.forEach((observer) => observer(value)),
|
33
|
+
};
|
34
|
+
}
|
35
|
+
|
36
|
+
interface Task {
|
37
|
+
ready: () => Promise<void>;
|
38
|
+
resolve: () => void;
|
39
|
+
}
|
40
|
+
|
41
|
+
interface State {
|
42
|
+
tasks: Array<Task>;
|
43
|
+
frameTimeElapsed: boolean;
|
44
|
+
onIdleCallback: WhenReady<void>;
|
45
|
+
onAnimationFrame: WhenReady<void>;
|
46
|
+
frameWorkStartTime: number | undefined;
|
47
|
+
idleDeadline: IdleDeadline | undefined;
|
48
|
+
}
|
49
|
+
|
50
|
+
export class BackgroundScheduler implements Scheduler {
|
51
|
+
state: State = {
|
52
|
+
tasks: [],
|
53
|
+
idleDeadline: undefined,
|
54
|
+
frameTimeElapsed: false,
|
55
|
+
onIdleCallback: whenReady(),
|
56
|
+
onAnimationFrame: whenReady(),
|
57
|
+
frameWorkStartTime: undefined,
|
58
|
+
};
|
59
|
+
|
60
|
+
isTracking = false;
|
61
|
+
idleCallbackId: number | undefined;
|
62
|
+
lastCallTime = 0;
|
63
|
+
lastResult = false;
|
64
|
+
globalId = 0;
|
65
|
+
running = new Set<number>();
|
66
|
+
callbacks: Array<() => void> = [];
|
67
|
+
promiseEscapeId: number | undefined;
|
68
|
+
|
69
|
+
scheduleTask(task: () => void) {
|
70
|
+
this.yieldBackgroundOrContinue().then(() => {
|
71
|
+
task();
|
72
|
+
});
|
73
|
+
}
|
74
|
+
|
75
|
+
createTask(): Task {
|
76
|
+
const wr = whenReady<void>();
|
77
|
+
const item = { ready: wr.promise, resolve: wr.resolve };
|
78
|
+
this.state.tasks.push(item);
|
79
|
+
if (this.state.tasks.length === 1) {
|
80
|
+
this.startTracking();
|
81
|
+
}
|
82
|
+
return item;
|
83
|
+
}
|
84
|
+
|
85
|
+
startTracking(): void {
|
86
|
+
if (this.isTracking) {
|
87
|
+
return;
|
88
|
+
}
|
89
|
+
|
90
|
+
this.isTracking = true;
|
91
|
+
|
92
|
+
const reset = () => {
|
93
|
+
this.state.idleDeadline = undefined;
|
94
|
+
this.state.frameTimeElapsed = false;
|
95
|
+
this.state.frameWorkStartTime = undefined;
|
96
|
+
};
|
97
|
+
|
98
|
+
const loop = () => {
|
99
|
+
if (typeof requestIdleCallback !== "undefined") {
|
100
|
+
this.idleCallbackId = requestIdleCallback((deadline) => {
|
101
|
+
reset();
|
102
|
+
this.state.idleDeadline = deadline;
|
103
|
+
this.state.onIdleCallback.resolve();
|
104
|
+
this.state.onIdleCallback = whenReady();
|
105
|
+
});
|
106
|
+
}
|
107
|
+
|
108
|
+
const cb = () => {
|
109
|
+
reset();
|
110
|
+
this.state.onAnimationFrame.resolve();
|
111
|
+
this.state.onAnimationFrame = whenReady();
|
112
|
+
if (this.state.tasks.length === 0) {
|
113
|
+
this.isTracking = false;
|
114
|
+
if (typeof cancelIdleCallback !== "undefined") {
|
115
|
+
cancelIdleCallback(this.idleCallbackId);
|
116
|
+
}
|
117
|
+
} else {
|
118
|
+
loop();
|
119
|
+
}
|
120
|
+
};
|
121
|
+
|
122
|
+
if (typeof requestAnimationFrame !== "undefined") {
|
123
|
+
requestAnimationFrame(cb);
|
124
|
+
} else {
|
125
|
+
setTimeout(cb, 0);
|
126
|
+
}
|
127
|
+
};
|
128
|
+
|
129
|
+
loop();
|
130
|
+
}
|
131
|
+
|
132
|
+
removeTask(task: Task) {
|
133
|
+
const index = this.state.tasks.indexOf(task);
|
134
|
+
if (index !== -1) {
|
135
|
+
this.state.tasks.splice(index, 1);
|
136
|
+
}
|
137
|
+
}
|
138
|
+
|
139
|
+
nextTask() {
|
140
|
+
if (this.state.tasks.length > 0) {
|
141
|
+
this.state.tasks[0]!.resolve();
|
142
|
+
}
|
143
|
+
}
|
144
|
+
|
145
|
+
isTimeToYield(): boolean {
|
146
|
+
const now = Date.now();
|
147
|
+
|
148
|
+
if (!this.lastResult && now - this.lastCallTime === 0) {
|
149
|
+
return this.lastResult;
|
150
|
+
}
|
151
|
+
|
152
|
+
this.lastCallTime = now;
|
153
|
+
this.lastResult =
|
154
|
+
now >= this.calculateDeadline() ||
|
155
|
+
(typeof navigator !== "undefined" && navigator.scheduling?.isInputPending?.() === true);
|
156
|
+
|
157
|
+
if (this.lastResult) {
|
158
|
+
this.state.frameTimeElapsed = true;
|
159
|
+
}
|
160
|
+
|
161
|
+
return this.lastResult;
|
162
|
+
}
|
163
|
+
|
164
|
+
calculateDeadline(): number {
|
165
|
+
if (this.state.frameWorkStartTime === undefined) {
|
166
|
+
return -1;
|
167
|
+
}
|
168
|
+
|
169
|
+
const idleDeadline =
|
170
|
+
this.state.idleDeadline === undefined
|
171
|
+
? Number.MAX_SAFE_INTEGER
|
172
|
+
: Date.now() + this.state.idleDeadline.timeRemaining();
|
173
|
+
|
174
|
+
return Math.min(this.state.frameWorkStartTime + 5, idleDeadline);
|
175
|
+
}
|
176
|
+
|
177
|
+
requestPromiseEscape(callback: () => void): number {
|
178
|
+
const id = this.globalId;
|
179
|
+
|
180
|
+
this.running.add(id);
|
181
|
+
|
182
|
+
Promise.resolve().then(() => {
|
183
|
+
Promise.resolve().then(() => {
|
184
|
+
if (this.running.has(id)) {
|
185
|
+
callback();
|
186
|
+
this.running.delete(id);
|
187
|
+
}
|
188
|
+
});
|
189
|
+
});
|
190
|
+
|
191
|
+
this.globalId += 1;
|
192
|
+
|
193
|
+
return id;
|
194
|
+
}
|
195
|
+
|
196
|
+
cancelPromiseEscape(id: number | undefined): void {
|
197
|
+
if (id !== undefined) {
|
198
|
+
this.running.delete(id);
|
199
|
+
}
|
200
|
+
}
|
201
|
+
|
202
|
+
requestNextTask(callback: () => void): void {
|
203
|
+
if (this.callbacks.length === 0) {
|
204
|
+
const channel = new MessageChannel();
|
205
|
+
channel.port2.postMessage(undefined);
|
206
|
+
// @ts-expect-error
|
207
|
+
channel.port1.onmessage = (): void => {
|
208
|
+
channel.port1.close();
|
209
|
+
channel.port2.close();
|
210
|
+
|
211
|
+
const callbacksCopy = this.callbacks;
|
212
|
+
this.callbacks = [];
|
213
|
+
for (const callback of callbacksCopy) {
|
214
|
+
callback();
|
215
|
+
}
|
216
|
+
};
|
217
|
+
}
|
218
|
+
|
219
|
+
this.callbacks.push(callback);
|
220
|
+
}
|
221
|
+
|
222
|
+
async yieldControl(): Promise<void> {
|
223
|
+
this.cancelPromiseEscape(this.promiseEscapeId);
|
224
|
+
|
225
|
+
const task = this.createTask();
|
226
|
+
|
227
|
+
await this.schedule();
|
228
|
+
|
229
|
+
if (this.state.tasks[0] !== task) {
|
230
|
+
await task.ready();
|
231
|
+
if (this.isTimeToYield()) {
|
232
|
+
await this.schedule();
|
233
|
+
}
|
234
|
+
}
|
235
|
+
|
236
|
+
this.removeTask(task);
|
237
|
+
|
238
|
+
this.cancelPromiseEscape(this.promiseEscapeId);
|
239
|
+
|
240
|
+
this.promiseEscapeId = this.requestPromiseEscape(() => {
|
241
|
+
this.nextTask();
|
242
|
+
});
|
243
|
+
}
|
244
|
+
|
245
|
+
async schedule(): Promise<void> {
|
246
|
+
if (this.state.frameTimeElapsed) {
|
247
|
+
await this.state.onAnimationFrame.promise();
|
248
|
+
}
|
249
|
+
|
250
|
+
if (typeof requestIdleCallback === "undefined") {
|
251
|
+
await new Promise<void>((resolve) => this.requestNextTask(resolve));
|
252
|
+
|
253
|
+
if (typeof navigator !== "undefined" && navigator.scheduling?.isInputPending?.() === true) {
|
254
|
+
await this.schedule();
|
255
|
+
} else if (this.state.frameWorkStartTime === undefined) {
|
256
|
+
this.state.frameWorkStartTime = Date.now();
|
257
|
+
}
|
258
|
+
} else {
|
259
|
+
await this.state.onIdleCallback.promise();
|
260
|
+
|
261
|
+
if (this.state.frameWorkStartTime === undefined) {
|
262
|
+
this.state.frameWorkStartTime = Date.now();
|
263
|
+
}
|
264
|
+
}
|
265
|
+
}
|
266
|
+
|
267
|
+
yieldBackgroundOrContinue(): Promise<void> {
|
268
|
+
if (this.isTimeToYield()) {
|
269
|
+
return this.yieldControl();
|
270
|
+
}
|
271
|
+
|
272
|
+
return Promise.resolve();
|
273
|
+
}
|
274
|
+
}
|
275
|
+
|
276
|
+
export const backgroundScheduler = new BackgroundScheduler();
|
package/global.d.ts
CHANGED
@@ -118,6 +118,10 @@ import { Finalizer } from "@fncts/io/Scope/Finalizer";
|
|
118
118
|
* @tsplus global
|
119
119
|
*/
|
120
120
|
import { ScopedRef } from "@fncts/io/ScopedRef/definition";
|
121
|
+
/**
|
122
|
+
* @tsplus global
|
123
|
+
*/
|
124
|
+
import { Semaphore } from "@fncts/io/Semaphore";
|
121
125
|
/**
|
122
126
|
* @tsplus global
|
123
127
|
*/
|
@@ -0,0 +1,47 @@
|
|
1
|
+
import type { Scheduler } from "@fncts/io/internal/Scheduler";
|
2
|
+
interface IdleDeadline {
|
3
|
+
timeRemaining(): number;
|
4
|
+
readonly didTimeout: boolean;
|
5
|
+
}
|
6
|
+
interface WhenReady<T> {
|
7
|
+
promise: () => Promise<T>;
|
8
|
+
resolve: (value: T) => void;
|
9
|
+
}
|
10
|
+
interface Task {
|
11
|
+
ready: () => Promise<void>;
|
12
|
+
resolve: () => void;
|
13
|
+
}
|
14
|
+
interface State {
|
15
|
+
tasks: Array<Task>;
|
16
|
+
frameTimeElapsed: boolean;
|
17
|
+
onIdleCallback: WhenReady<void>;
|
18
|
+
onAnimationFrame: WhenReady<void>;
|
19
|
+
frameWorkStartTime: number | undefined;
|
20
|
+
idleDeadline: IdleDeadline | undefined;
|
21
|
+
}
|
22
|
+
export declare class BackgroundScheduler implements Scheduler {
|
23
|
+
state: State;
|
24
|
+
isTracking: boolean;
|
25
|
+
idleCallbackId: number | undefined;
|
26
|
+
lastCallTime: number;
|
27
|
+
lastResult: boolean;
|
28
|
+
globalId: number;
|
29
|
+
running: Set<number>;
|
30
|
+
callbacks: Array<() => void>;
|
31
|
+
promiseEscapeId: number | undefined;
|
32
|
+
scheduleTask(task: () => void): void;
|
33
|
+
createTask(): Task;
|
34
|
+
startTracking(): void;
|
35
|
+
removeTask(task: Task): void;
|
36
|
+
nextTask(): void;
|
37
|
+
isTimeToYield(): boolean;
|
38
|
+
calculateDeadline(): number;
|
39
|
+
requestPromiseEscape(callback: () => void): number;
|
40
|
+
cancelPromiseEscape(id: number | undefined): void;
|
41
|
+
requestNextTask(callback: () => void): void;
|
42
|
+
yieldControl(): Promise<void>;
|
43
|
+
schedule(): Promise<void>;
|
44
|
+
yieldBackgroundOrContinue(): Promise<void>;
|
45
|
+
}
|
46
|
+
export declare const backgroundScheduler: BackgroundScheduler;
|
47
|
+
export {};
|