@fncts/io 0.0.30 → 0.0.32
Sign up to get free protection for your applications and to get access to all the features.
- package/Channel/api/runScoped.d.ts +1 -0
- package/Channel/api.d.ts +1 -0
- package/Channel/internal/ChannelExecutor.d.ts +2 -2
- package/Fiber/FiberMessage.d.ts +5 -5
- package/IO/api/blocking.d.ts +7 -0
- package/IO/api/provideSomeLayer.d.ts +1 -2
- package/IO/api/zipConcurrent.d.ts +2 -1
- package/IO/runtime.d.ts +4 -2
- package/IO.d.ts +1 -0
- package/Ref/Synchronized/definition.d.ts +3 -3
- package/Semaphore.d.ts +30 -0
- package/Stream/api.d.ts +24 -4
- 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/Channel/api/runScoped.cjs +19 -3
- package/_cjs/Channel/api/runScoped.cjs.map +1 -1
- package/_cjs/Channel/api/toPull.cjs +1 -1
- package/_cjs/Channel/api/toPull.cjs.map +1 -1
- package/_cjs/Channel/api.cjs +3 -2
- package/_cjs/Channel/api.cjs.map +1 -1
- package/_cjs/Channel/internal/ChannelExecutor.cjs +61 -52
- package/_cjs/Channel/internal/ChannelExecutor.cjs.map +1 -1
- package/_cjs/Fiber/FiberMessage.cjs +12 -11
- package/_cjs/Fiber/FiberMessage.cjs.map +1 -1
- package/_cjs/Fiber/FiberRuntime.cjs +28 -27
- package/_cjs/Fiber/FiberRuntime.cjs.map +1 -1
- package/_cjs/FiberScope/definition.cjs +2 -2
- package/_cjs/FiberScope/definition.cjs.map +1 -1
- package/_cjs/Hub/api.cjs.map +1 -1
- package/_cjs/{RuntimeConfig.cjs → IO/api/blocking.cjs} +13 -5
- package/_cjs/IO/api/blocking.cjs.map +1 -0
- package/_cjs/IO/api/provideSomeLayer.cjs.map +1 -1
- package/_cjs/IO/api/raceWith.cjs +9 -12
- package/_cjs/IO/api/raceWith.cjs.map +1 -1
- package/_cjs/IO/api/stateful.cjs +3 -3
- package/_cjs/IO/api/stateful.cjs.map +1 -1
- package/_cjs/IO/api/withChildren.cjs +5 -4
- package/_cjs/IO/api/withChildren.cjs.map +1 -1
- package/_cjs/IO/api/zipConcurrent.cjs +23 -23
- package/_cjs/IO/api/zipConcurrent.cjs.map +1 -1
- package/_cjs/IO/runtime.cjs +8 -8
- package/_cjs/IO/runtime.cjs.map +1 -1
- package/_cjs/IO.cjs +11 -0
- package/_cjs/IO.cjs.map +1 -1
- package/_cjs/Queue/api/filterInputIO.cjs.map +1 -1
- package/_cjs/Queue/api/filterOutputIO.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 +101 -61
- 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/Cached/definition.mjs.map +1 -1
- 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/Channel/api/runScoped.mjs +19 -3
- package/_mjs/Channel/api/runScoped.mjs.map +1 -1
- package/_mjs/Channel/api/toPull.mjs +1 -1
- package/_mjs/Channel/api/toPull.mjs.map +1 -1
- package/_mjs/Channel/api.mjs +3 -2
- package/_mjs/Channel/api.mjs.map +1 -1
- package/_mjs/Channel/internal/ChannelExecutor.mjs +61 -52
- package/_mjs/Channel/internal/ChannelExecutor.mjs.map +1 -1
- package/_mjs/Fiber/FiberMessage.mjs +5 -5
- package/_mjs/Fiber/FiberMessage.mjs.map +1 -1
- package/_mjs/Fiber/FiberRuntime.mjs +28 -27
- package/_mjs/Fiber/FiberRuntime.mjs.map +1 -1
- package/_mjs/FiberScope/definition.mjs +2 -2
- package/_mjs/FiberScope/definition.mjs.map +1 -1
- package/_mjs/Hub/api.mjs.map +1 -1
- package/_mjs/IO/api/blocking.mjs +12 -0
- package/_mjs/IO/api/blocking.mjs.map +1 -0
- package/_mjs/IO/api/provideSomeLayer.mjs.map +1 -1
- package/_mjs/IO/api/raceWith.mjs +9 -12
- package/_mjs/IO/api/raceWith.mjs.map +1 -1
- package/_mjs/IO/api/stateful.mjs +3 -3
- package/_mjs/IO/api/stateful.mjs.map +1 -1
- package/_mjs/IO/api/withChildren.mjs +5 -4
- package/_mjs/IO/api/withChildren.mjs.map +1 -1
- package/_mjs/IO/api/zipConcurrent.mjs +23 -23
- package/_mjs/IO/api/zipConcurrent.mjs.map +1 -1
- package/_mjs/IO/runtime.mjs +6 -6
- package/_mjs/IO/runtime.mjs.map +1 -1
- package/_mjs/IO.mjs +1 -0
- package/_mjs/IO.mjs.map +1 -1
- package/_mjs/Queue/api/filterInputIO.mjs.map +1 -1
- package/_mjs/Queue/api/filterOutputIO.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 +93 -57
- 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/Cached/definition.ts +1 -1
- package/_src/Channel/api/mapOutConcurrentIO.ts +1 -1
- package/_src/Channel/api/mergeAllWith.ts +1 -1
- package/_src/Channel/api/runScoped.ts +30 -5
- package/_src/Channel/api/toPull.ts +1 -1
- package/_src/Channel/api.ts +1 -0
- package/_src/Channel/internal/ChannelExecutor.ts +24 -20
- package/_src/Fiber/FiberMessage.ts +5 -5
- package/_src/Fiber/FiberRuntime.ts +1 -1
- package/_src/FiberRefs/api.ts +1 -1
- package/_src/Hub/api.ts +1 -1
- package/_src/IO/api/blocking.ts +9 -0
- package/_src/IO/api/provideSomeLayer.ts +1 -3
- package/_src/IO/api/raceWith.ts +1 -3
- package/_src/IO/api/stateful.ts +1 -1
- package/_src/IO/api/zipConcurrent.ts +26 -30
- package/_src/IO/runtime.ts +4 -4
- package/_src/IO.ts +1 -0
- package/_src/Queue/api/filterInputIO.ts +1 -1
- package/_src/Queue/api/filterOutputIO.ts +1 -1
- package/_src/Ref/Synchronized/constructors.ts +2 -2
- package/_src/Ref/Synchronized/definition.ts +1 -1
- package/_src/Semaphore.ts +81 -0
- package/_src/State/definition.ts +1 -1
- package/_src/Stream/api.ts +58 -7
- package/_src/Subject/Atomic.ts +1 -1
- package/_src/SubscriptionRef.ts +2 -2
- package/_src/global.ts +4 -4
- package/_src/index.ts +2 -2
- package/_src/internal/BackgroundScheduler.ts +276 -0
- package/global.d.ts +4 -4
- package/index.d.ts +2 -2
- package/internal/BackgroundScheduler.d.ts +47 -0
- package/package.json +3 -3
- package/RuntimeConfig.d.ts +0 -11
- package/_cjs/RuntimeConfig.cjs.map +0 -1
- package/_mjs/RuntimeConfig.mjs +0 -3
- package/_mjs/RuntimeConfig.mjs.map +0 -1
- package/_src/RuntimeConfig.ts +0 -6
package/_src/Stream/api.ts
CHANGED
@@ -812,9 +812,10 @@ function combineChunksProducer<Err, Elem>(
|
|
812
812
|
): Channel<never, Err, Conc<Elem>, unknown, never, never, any> {
|
813
813
|
return Channel.fromIO(latch.take).zipRight(
|
814
814
|
Channel.readWithCause(
|
815
|
-
(chunk) =>
|
815
|
+
(chunk: Conc<Elem>) =>
|
816
|
+
Channel.fromIO(handoff.offer(Take.chunk(chunk))).zipRight(combineChunksProducer(handoff, latch)),
|
816
817
|
(cause) => Channel.fromIO(handoff.offer(Take.failCause(cause))),
|
817
|
-
() => Channel.fromIO(handoff.offer(Take.end))
|
818
|
+
() => Channel.fromIO(handoff.offer(Take.end)),
|
818
819
|
),
|
819
820
|
);
|
820
821
|
}
|
@@ -991,6 +992,13 @@ export function debounce(duration: Lazy<Duration>, __tsplusTrace?: string) {
|
|
991
992
|
};
|
992
993
|
}
|
993
994
|
|
995
|
+
/**
|
996
|
+
* @tsplus static fncts.io.StreamOps defer
|
997
|
+
*/
|
998
|
+
export function defer<R, E, A>(self: Lazy<Stream<R, E, A>>): Stream<R, E, A> {
|
999
|
+
return new Stream(Channel.defer(self().channel));
|
1000
|
+
}
|
1001
|
+
|
994
1002
|
function defaultIfEmptyWriter<R, E, A, R1, E1, B>(
|
995
1003
|
fb: Stream<R1, E1, B>,
|
996
1004
|
__tsplusTrace?: string,
|
@@ -1092,7 +1100,7 @@ export function distributedWithDynamic<E, A>(
|
|
1092
1100
|
);
|
1093
1101
|
const add = Δ(
|
1094
1102
|
Do((Δ) => {
|
1095
|
-
const queuesLock = Δ(
|
1103
|
+
const queuesLock = Δ(Semaphore(1));
|
1096
1104
|
const newQueue = Δ(
|
1097
1105
|
Ref.make<UIO<readonly [symbol, Queue<Exit<Maybe<E>, A>>]>>(
|
1098
1106
|
Do((Δ) => {
|
@@ -2189,14 +2197,27 @@ function mapIOLoop<R, E, A, R1, E1, B>(
|
|
2189
2197
|
*
|
2190
2198
|
* @note This combinator destroys the chunking structure. It's recommended to use chunkN afterwards.
|
2191
2199
|
*
|
2192
|
-
* @tsplus pipeable fncts.io.Stream
|
2200
|
+
* @tsplus pipeable fncts.io.Stream mapIOConcurrently
|
2193
2201
|
*/
|
2194
|
-
export function
|
2202
|
+
export function mapIOConcurrently<A, R1, E1, B>(n: number, f: (a: A) => IO<R1, E1, B>, __tsplusTrace?: string) {
|
2195
2203
|
return <R, E>(stream: Stream<R, E, A>): Stream<R | R1, E | E1, B> => {
|
2196
2204
|
return new Stream(stream.channel.concatMap(Channel.writeChunk).mapOutConcurrentIO(n, f).mapOut(Conc.single));
|
2197
2205
|
};
|
2198
2206
|
}
|
2199
2207
|
|
2208
|
+
/**
|
2209
|
+
* @tsplus pipeable fncts.io.Stream mapIOConcurrentlyUnordered
|
2210
|
+
*/
|
2211
|
+
export function mapIOConcurrentlyUnordered<A, R1, E1, B>(n: number, f: (a: A) => IO<R1, E1, B>) {
|
2212
|
+
return <R, E>(self: Stream<R, E, A>): Stream<R | R1, E | E1, B> => {
|
2213
|
+
return self.pipeThroughChannelOrFail(
|
2214
|
+
Channel.id<never, Conc<A>, any>()
|
2215
|
+
.concatMap((chunk) => Channel.writeChunk(chunk))
|
2216
|
+
.mergeMap((inp) => Stream.fromIO(f(inp)).channel, n, 16),
|
2217
|
+
);
|
2218
|
+
};
|
2219
|
+
}
|
2220
|
+
|
2200
2221
|
/**
|
2201
2222
|
* Maps each element of this stream to another stream and returns the
|
2202
2223
|
* non-deterministic merge of those streams, executing up to `n` inner streams
|
@@ -2243,7 +2264,7 @@ export function mergeEither<R1, E1, B>(fb: Stream<R1, E1, B>, __tsplusTrace?: st
|
|
2243
2264
|
};
|
2244
2265
|
}
|
2245
2266
|
|
2246
|
-
|
2267
|
+
function mergeWithHandler<R, E>(
|
2247
2268
|
terminate: boolean,
|
2248
2269
|
__tsplusTrace?: string,
|
2249
2270
|
): (exit: Exit<E, unknown>) => MergeDecision<R, E, unknown, E, unknown> {
|
@@ -2366,6 +2387,15 @@ export function pipeThrough<A, R1, E1, L, Z>(sa: Sink<R1, E1, A, L, Z>, __tsplus
|
|
2366
2387
|
};
|
2367
2388
|
}
|
2368
2389
|
|
2390
|
+
/**
|
2391
|
+
* @tsplus pipeable fncts.io.Stream pipeThroughChannelOrFail
|
2392
|
+
*/
|
2393
|
+
export function pipeThroughChannelOrFail<A, R1, E1, B>(channel: Channel<R1, never, Conc<A>, any, E1, Conc<B>, any>) {
|
2394
|
+
return <R, E>(self: Stream<R, E, A>): Stream<R | R1, E | E1, B> => {
|
2395
|
+
return new Stream(self.channel >>> channel);
|
2396
|
+
};
|
2397
|
+
}
|
2398
|
+
|
2369
2399
|
/**
|
2370
2400
|
* Provides the stream with its required environment, which eliminates
|
2371
2401
|
* its dependency on `R`.
|
@@ -2603,6 +2633,27 @@ export function runIntoQueueScoped<RA, RB, EA, EB, E1, A, B>(
|
|
2603
2633
|
};
|
2604
2634
|
}
|
2605
2635
|
|
2636
|
+
/**
|
2637
|
+
* Like runIntoQueue, but provides the result as a scoped IO
|
2638
|
+
* to allow for scope composition.
|
2639
|
+
*
|
2640
|
+
* @tsplus pipeable fncts.io.Stream runIntoQueueElementsScoped
|
2641
|
+
*/
|
2642
|
+
export function runIntoQueueElementsScoped<E, A>(queue: Lazy<Queue.Enqueue<Exit<Maybe<E>, A>>>) {
|
2643
|
+
return <R>(self: Stream<R, E, A>): IO<R | Scope, never, void> => {
|
2644
|
+
return IO.defer(() => {
|
2645
|
+
const queue0 = queue();
|
2646
|
+
const writer: Channel<R, E, Conc<A>, any, never, Exit<Maybe<E>, A>, any> = Channel.readWithCause(
|
2647
|
+
(inp: Conc<A>) => Channel.fromIO(queue0.offerAll(inp.map((a) => Exit.succeed(a)))) > writer,
|
2648
|
+
(cause) => Channel.fromIO(queue0.offer(Exit.failCause(cause.map((e) => Just(e))))),
|
2649
|
+
() => Channel.fromIO(queue0.offer(Exit.fail(Nothing()))),
|
2650
|
+
);
|
2651
|
+
|
2652
|
+
return (self.channel >>> writer).drain.runScoped.asUnit;
|
2653
|
+
});
|
2654
|
+
};
|
2655
|
+
}
|
2656
|
+
|
2606
2657
|
/**
|
2607
2658
|
* Like `Stream#runIntoHub`, but provides the result as a `Managed` to allow for scope
|
2608
2659
|
* composition.
|
@@ -2947,7 +2998,7 @@ export function toQueueOfElements(capacity = 2, __tsplusTrace?: string) {
|
|
2947
2998
|
return <R, E, A>(stream: Stream<R, E, A>): IO<R | Scope, never, Queue.Dequeue<Exit<Maybe<E>, A>>> => {
|
2948
2999
|
return Do((Δ) => {
|
2949
3000
|
const queue = Δ(IO.acquireRelease(Queue.makeBounded<Exit<Maybe<E>, A>>(capacity), (_) => _.shutdown));
|
2950
|
-
Δ(stream.
|
3001
|
+
Δ(stream.runIntoQueueElementsScoped(queue).forkScoped);
|
2951
3002
|
return queue;
|
2952
3003
|
});
|
2953
3004
|
};
|
package/_src/Subject/Atomic.ts
CHANGED
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
@@ -99,10 +99,6 @@ import { Random } from "@fncts/io/Random/definition";
|
|
99
99
|
* @tsplus global
|
100
100
|
*/
|
101
101
|
import { PRef, Ref } from "@fncts/io/Ref/definition";
|
102
|
-
/**
|
103
|
-
* @tsplus global
|
104
|
-
*/
|
105
|
-
import { RuntimeConfig } from "@fncts/io/RuntimeConfig";
|
106
102
|
/**
|
107
103
|
* @tsplus global
|
108
104
|
*/
|
@@ -119,6 +115,10 @@ import { Finalizer } from "@fncts/io/Scope/Finalizer";
|
|
119
115
|
* @tsplus global
|
120
116
|
*/
|
121
117
|
import { ScopedRef } from "@fncts/io/ScopedRef/definition";
|
118
|
+
/**
|
119
|
+
* @tsplus global
|
120
|
+
*/
|
121
|
+
import { Semaphore } from "@fncts/io/Semaphore";
|
122
122
|
/**
|
123
123
|
* @tsplus global
|
124
124
|
*/
|
package/_src/index.ts
CHANGED
@@ -5,7 +5,6 @@ export type {} from "./Channel.js";
|
|
5
5
|
export type {} from "./Clock.js";
|
6
6
|
export type {} from "./Console.js";
|
7
7
|
export type {} from "./CountdownLatch.js";
|
8
|
-
export type {} from "./demo.js";
|
9
8
|
export type {} from "./Fiber.js";
|
10
9
|
export type {} from "./FiberDescriptor.js";
|
11
10
|
export type {} from "./FiberRef.js";
|
@@ -22,18 +21,19 @@ export type {} from "./Layer.js";
|
|
22
21
|
export type {} from "./Logger.js";
|
23
22
|
export type {} from "./LogLevel.js";
|
24
23
|
export type {} from "./LogSpan.js";
|
24
|
+
export type {} from "./MVar.js";
|
25
25
|
export type {} from "./Push.js";
|
26
26
|
export type {} from "./Queue.js";
|
27
27
|
export type {} from "./Random.js";
|
28
28
|
export type {} from "./Ref.js";
|
29
29
|
export type {} from "./RefSubject.js";
|
30
30
|
export type {} from "./Reloadable.js";
|
31
|
-
export type {} from "./RuntimeConfig.js";
|
32
31
|
export type {} from "./RuntimeFlag.js";
|
33
32
|
export type {} from "./RuntimeFlags.js";
|
34
33
|
export type {} from "./Schedule.js";
|
35
34
|
export type {} from "./Scope.js";
|
36
35
|
export type {} from "./ScopedRef.js";
|
36
|
+
export type {} from "./Semaphore.js";
|
37
37
|
export type {} from "./Sink.js";
|
38
38
|
export type {} from "./State.js";
|
39
39
|
export type {} from "./STM.js";
|
@@ -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
@@ -98,10 +98,6 @@ import { Random } from "@fncts/io/Random/definition";
|
|
98
98
|
* @tsplus global
|
99
99
|
*/
|
100
100
|
import { PRef, Ref } from "@fncts/io/Ref/definition";
|
101
|
-
/**
|
102
|
-
* @tsplus global
|
103
|
-
*/
|
104
|
-
import { RuntimeConfig } from "@fncts/io/RuntimeConfig";
|
105
101
|
/**
|
106
102
|
* @tsplus global
|
107
103
|
*/
|
@@ -118,6 +114,10 @@ import { Finalizer } from "@fncts/io/Scope/Finalizer";
|
|
118
114
|
* @tsplus global
|
119
115
|
*/
|
120
116
|
import { ScopedRef } from "@fncts/io/ScopedRef/definition";
|
117
|
+
/**
|
118
|
+
* @tsplus global
|
119
|
+
*/
|
120
|
+
import { Semaphore } from "@fncts/io/Semaphore";
|
121
121
|
/**
|
122
122
|
* @tsplus global
|
123
123
|
*/
|
package/index.d.ts
CHANGED
@@ -4,7 +4,6 @@ export type {} from "./Channel.js";
|
|
4
4
|
export type {} from "./Clock.js";
|
5
5
|
export type {} from "./Console.js";
|
6
6
|
export type {} from "./CountdownLatch.js";
|
7
|
-
export type {} from "./demo.js";
|
8
7
|
export type {} from "./Fiber.js";
|
9
8
|
export type {} from "./FiberDescriptor.js";
|
10
9
|
export type {} from "./FiberRef.js";
|
@@ -21,18 +20,19 @@ export type {} from "./Layer.js";
|
|
21
20
|
export type {} from "./Logger.js";
|
22
21
|
export type {} from "./LogLevel.js";
|
23
22
|
export type {} from "./LogSpan.js";
|
23
|
+
export type {} from "./MVar.js";
|
24
24
|
export type {} from "./Push.js";
|
25
25
|
export type {} from "./Queue.js";
|
26
26
|
export type {} from "./Random.js";
|
27
27
|
export type {} from "./Ref.js";
|
28
28
|
export type {} from "./RefSubject.js";
|
29
29
|
export type {} from "./Reloadable.js";
|
30
|
-
export type {} from "./RuntimeConfig.js";
|
31
30
|
export type {} from "./RuntimeFlag.js";
|
32
31
|
export type {} from "./RuntimeFlags.js";
|
33
32
|
export type {} from "./Schedule.js";
|
34
33
|
export type {} from "./Scope.js";
|
35
34
|
export type {} from "./ScopedRef.js";
|
35
|
+
export type {} from "./Semaphore.js";
|
36
36
|
export type {} from "./Sink.js";
|
37
37
|
export type {} from "./State.js";
|
38
38
|
export type {} from "./STM.js";
|
@@ -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 {};
|
package/package.json
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
{
|
2
2
|
"name": "@fncts/io",
|
3
|
-
"version": "0.0.
|
3
|
+
"version": "0.0.32",
|
4
4
|
"dependencies": {
|
5
|
-
"@fncts/base": "0.0.
|
6
|
-
"@fncts/transformers": "0.0.
|
5
|
+
"@fncts/base": "0.0.27",
|
6
|
+
"@fncts/transformers": "0.0.5",
|
7
7
|
"@fncts/typelevel": "0.0.15"
|
8
8
|
},
|
9
9
|
"exports": {
|
package/RuntimeConfig.d.ts
DELETED
@@ -1,11 +0,0 @@
|
|
1
|
-
import { CaseClass } from "@fncts/base/data/CaseClass";
|
2
|
-
import { Cause } from "@fncts/base/data/Cause/definition";
|
3
|
-
import { Supervisor } from "@fncts/io/Supervisor/definition";
|
4
|
-
import { Logger } from "@fncts/io/Logger/definition";
|
5
|
-
export declare class RuntimeConfig extends CaseClass<{
|
6
|
-
readonly reportFailure: (e: Cause<unknown>) => void;
|
7
|
-
readonly supervisor: Supervisor<any>;
|
8
|
-
readonly yieldOpCount: number;
|
9
|
-
readonly logger: Logger<string, any>;
|
10
|
-
}> {
|
11
|
-
}
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"RuntimeConfig.cjs","mappings":";;;;;;;;;AAAM,MAAOA,aAAc,SAAQC,yBAKjC;AAAG","names":["RuntimeConfig","tsplus_module_1"],"sourceRoot":"","sources":["../_src/RuntimeConfig.ts"],"sourcesContent":[null]}
|
package/_mjs/RuntimeConfig.mjs
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"RuntimeConfig.mjs","mappings":";AAAA,OAAM,MAAOA,aAAc,SAAQC,yBAKjC","names":["RuntimeConfig","tsplus_module_1"],"sourceRoot":"","sources":["../_src/RuntimeConfig.ts"],"sourcesContent":[null]}
|