Skip to content

Commit 2e067b9

Browse files
committed
refactor: update schedulers
1 parent 7db5485 commit 2e067b9

20 files changed

Lines changed: 209 additions & 102 deletions

mangle-cache.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"addDep_": "_d",
3+
"batchTask_": "_k",
34
"batching_": "_c",
45
"data_": "_a",
56
"delete_": "_l",
@@ -15,10 +16,9 @@
1516
"resolveValue_": "_r",
1617
"resolveValueError_": "_R",
1718
"schedule_": "s",
19+
"schedulerTask_": "_j",
1820
"single_": "_s",
1921
"subs_": "_S",
20-
"task_": "_k",
21-
"tasks_": "_t",
2222
"upsert_": "_u",
2323
"value_": "_V",
2424
"valueMaybeDirty_": "_M",

src/batch.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,20 @@ import { context } from "./context";
22
import { UNIQUE_VALUE } from "./utils";
33

44
export type BatchTask<O extends object = object> = O & {
5-
task_: () => void;
5+
batchTask_: () => void;
66
};
77

8-
export const tasks: Set<BatchTask> = /* @__PURE__ */ (() => context.tasks_)();
8+
export const batchTasks: Set<BatchTask> = /* @__PURE__ */ (() => context.batchTask_)();
99

1010
export const batchStart = (): boolean => !context.batching_ && (context.batching_ = true);
1111

1212
export const batchFlush = (): void => {
1313
if (context.batching_) {
1414
let error: unknown = UNIQUE_VALUE;
15-
for (const task of tasks) {
16-
tasks.delete(task);
15+
for (const task of batchTasks) {
16+
batchTasks.delete(task);
1717
try {
18-
task.task_();
18+
task.batchTask_();
1919
} catch (e) {
2020
error = e;
2121
}

src/collections/reactiveArray.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { batchFlush, batchStart, tasks } from "../batch";
1+
import { batchFlush, batchStart, batchTasks } from "../batch";
22
import { type RemoveListener } from "../event";
33
import { writable } from "../readable";
44
import { type ReadableProvider, type OwnedWritable, type Readable } from "../typings";
@@ -162,7 +162,7 @@ export class OwnedReactiveArray<V> extends Array<V> implements ReadableProvider<
162162
}
163163
if (delete_.size) {
164164
const isBatchTop = batchStart();
165-
tasks.add(this.onDisposeValue_!);
165+
batchTasks.add(this.onDisposeValue_!);
166166
isBatchTop && batchFlush();
167167
}
168168
}

src/collections/reactiveMap.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { batchFlush, batchStart, type BatchTask, tasks } from "../batch";
1+
import { batchFlush, batchStart, type BatchTask, batchTasks } from "../batch";
22
import { type EventObject, on, type RemoveListener, send, size } from "../event";
33
import { writable } from "../readable";
44
import { type ReadableProvider, type OwnedWritable, type Readable } from "../typings";
@@ -34,7 +34,7 @@ export class OwnedReactiveMap<K, V> extends Map<K, V> implements ReadableProvide
3434
(this._onChanged_ ??= {
3535
delete_: new Set<K>(),
3636
upsert_: new Map<K, V>(),
37-
task_: () => {
37+
batchTask_: () => {
3838
if (this._onChanged_ && size(this._onChanged_)) {
3939
const { upsert_, delete_ } = this._onChanged_;
4040
if (upsert_.size > 0 || delete_.size > 0) {
@@ -95,7 +95,7 @@ export class OwnedReactiveMap<K, V> extends Map<K, V> implements ReadableProvide
9595
}
9696
if (delete_.size) {
9797
const isBatchTop = batchStart();
98-
tasks.add(this.onDisposeValue_);
98+
batchTasks.add(this.onDisposeValue_);
9999
isBatchTop && batchFlush();
100100
}
101101
}
@@ -125,12 +125,12 @@ export class OwnedReactiveMap<K, V> extends Map<K, V> implements ReadableProvide
125125
const isBatchTop = batchStart();
126126
if (this.onDisposeValue_) {
127127
this.onDisposeValue_.delete_.add(this.get(key)!);
128-
tasks.add(this.onDisposeValue_);
128+
batchTasks.add(this.onDisposeValue_);
129129
}
130130
if (this._onChanged_) {
131131
this._onChanged_.delete_.add(key);
132132
this._onChanged_.upsert_.delete(key);
133-
tasks.add(this._onChanged_);
133+
batchTasks.add(this._onChanged_);
134134
}
135135
this._notify_();
136136
isBatchTop && batchFlush();
@@ -145,12 +145,12 @@ export class OwnedReactiveMap<K, V> extends Map<K, V> implements ReadableProvide
145145
for (const [key, value] of this) {
146146
if (this.onDisposeValue_) {
147147
this.onDisposeValue_.delete_.add(value);
148-
tasks.add(this.onDisposeValue_);
148+
batchTasks.add(this.onDisposeValue_);
149149
}
150150
if (this._onChanged_) {
151151
this._onChanged_.delete_.add(key);
152152
this._onChanged_.upsert_.delete(key);
153-
tasks.add(this._onChanged_);
153+
batchTasks.add(this._onChanged_);
154154
}
155155
}
156156
}
@@ -188,7 +188,7 @@ export class OwnedReactiveMap<K, V> extends Map<K, V> implements ReadableProvide
188188
if (this._onChanged_) {
189189
this._onChanged_.upsert_.set(key, value);
190190
this._onChanged_.delete_.delete(key);
191-
tasks.add(this._onChanged_);
191+
batchTasks.add(this._onChanged_);
192192
}
193193
super.set(key, value);
194194
this._notify_();

src/collections/reactiveSet.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { batchFlush, batchStart, type BatchTask, tasks } from "../batch";
1+
import { batchFlush, batchStart, type BatchTask, batchTasks } from "../batch";
22
import { type EventObject, on, type RemoveListener, send, size } from "../event";
33
import { writable } from "../readable";
44
import { type ReadableProvider, type OwnedWritable, type Readable } from "../typings";
@@ -33,7 +33,7 @@ export class OwnedReactiveSet<V> extends Set<V> implements ReadableProvider<Read
3333
(this._onChanged_ ??= {
3434
delete_: new Set<V>(),
3535
upsert_: new Set<V>(),
36-
task_: () => {
36+
batchTask_: () => {
3737
if (this._onChanged_ && size(this._onChanged_)) {
3838
const { upsert_, delete_ } = this._onChanged_;
3939
if (upsert_.size > 0 || delete_.size > 0) {
@@ -92,7 +92,7 @@ export class OwnedReactiveSet<V> extends Set<V> implements ReadableProvider<Read
9292
}
9393
if (delete_.size) {
9494
const isBatchTop = batchStart();
95-
tasks.add(this.onDisposeValue_);
95+
batchTasks.add(this.onDisposeValue_);
9696
isBatchTop && batchFlush();
9797
}
9898
}
@@ -106,7 +106,7 @@ export class OwnedReactiveSet<V> extends Set<V> implements ReadableProvider<Read
106106
if (this._onChanged_) {
107107
this._onChanged_.upsert_.add(value);
108108
this._onChanged_.delete_.delete(value);
109-
tasks.add(this._onChanged_);
109+
batchTasks.add(this._onChanged_);
110110
}
111111
super.add(value);
112112
this._notify_();
@@ -120,12 +120,12 @@ export class OwnedReactiveSet<V> extends Set<V> implements ReadableProvider<Read
120120
const isBatchTop = batchStart();
121121
if (this.onDisposeValue_) {
122122
this.onDisposeValue_.delete_.add(value);
123-
tasks.add(this.onDisposeValue_);
123+
batchTasks.add(this.onDisposeValue_);
124124
}
125125
if (this._onChanged_) {
126126
this._onChanged_.delete_.add(value);
127127
this._onChanged_.upsert_.delete(value);
128-
tasks.add(this._onChanged_);
128+
batchTasks.add(this._onChanged_);
129129
}
130130
this._notify_();
131131
isBatchTop && batchFlush();
@@ -140,12 +140,12 @@ export class OwnedReactiveSet<V> extends Set<V> implements ReadableProvider<Read
140140
for (const value of this) {
141141
if (this.onDisposeValue_) {
142142
this.onDisposeValue_.delete_.add(value);
143-
tasks.add(this.onDisposeValue_);
143+
batchTasks.add(this.onDisposeValue_);
144144
}
145145
if (this._onChanged_) {
146146
this._onChanged_.delete_.add(value);
147147
this._onChanged_.upsert_.delete(value);
148-
tasks.add(this._onChanged_);
148+
batchTasks.add(this._onChanged_);
149149
}
150150
}
151151
}

src/collections/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export function onDisposeValue<V>(
1313
return on(
1414
(this.onDisposeValue_ ??= {
1515
delete_: new Set<V>(),
16-
task_: () => {
16+
batchTask_: () => {
1717
if (this.onDisposeValue_ && size(this.onDisposeValue_)) {
1818
const { delete_ } = this.onDisposeValue_;
1919
if (delete_.size) {

src/context.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { BRAND } from "./utils";
33

44
export interface Context {
55
batching_: boolean;
6-
readonly tasks_: Set<BatchTask>;
6+
readonly batchTask_: Set<BatchTask>;
77
}
88

99
declare const globalThis: {
@@ -13,5 +13,5 @@ declare const globalThis: {
1313
export const context: Context = /* @__PURE__ */ (() =>
1414
(globalThis[BRAND] ??= {
1515
batching_: false,
16-
tasks_: new Set<BatchTask>(),
16+
batchTask_: new Set<BatchTask>(),
1717
}))();

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export type {
1919

2020
export { type Listener, type RemoveListener } from "./event";
2121

22-
export { SyncScheduler, PromiseScheduler, type Scheduler } from "./schedulers";
22+
export { SyncScheduler, MicrotaskScheduler, asyncScheduler, type Scheduler, type SchedulerFlush } from "./schedulers";
2323

2424
export { batch, batchFlush, batchStart } from "./batch";
2525

src/readable.ts

Lines changed: 29 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { batchFlush, batchStart, type BatchTask, tasks } from "./batch";
1+
import { batchFlush, batchStart, type BatchTask, batchTasks } from "./batch";
22
import { type EventObject, off, on, send, size } from "./event";
33
import { SyncScheduler, type Scheduler } from "./schedulers";
44
import {
@@ -16,7 +16,6 @@ import { BRAND, strictEqual, UNIQUE_VALUE } from "./utils";
1616

1717
interface Subs<TValue> extends EventObject<TValue> {
1818
lastVersion_: Version;
19-
schedule_: () => void;
2019
}
2120

2221
export type Deps = Map<ReadableImpl, Version>;
@@ -95,7 +94,7 @@ export class ReadableImpl<TValue = any> implements BatchTask {
9594
/**
9695
* @internal
9796
*/
98-
public subs?: Map<Scheduler, Subs<TValue>>;
97+
public subs_?: Map<Scheduler, Subs<TValue>>;
9998

10099
public get version(): Version {
101100
this.get();
@@ -138,7 +137,7 @@ export class ReadableImpl<TValue = any> implements BatchTask {
138137
/**
139138
* @internal
140139
*/
141-
private version_: Version = -1;
140+
public version_: Version = -1;
142141

143142
/**
144143
* @internal
@@ -163,10 +162,22 @@ export class ReadableImpl<TValue = any> implements BatchTask {
163162
}
164163

165164
/** @internal */
166-
public task_(): void {
167-
if (this.subs) {
168-
for (const [scheduler, subs] of this.subs) {
169-
scheduler(subs.schedule_);
165+
public batchTask_(): void {
166+
if (this.subs_) {
167+
for (const scheduler of this.subs_.keys()) {
168+
scheduler(this);
169+
}
170+
}
171+
}
172+
173+
/** @internal */
174+
public schedulerTask_(scheduler: Scheduler): void {
175+
const subs = this.subs_?.get(scheduler);
176+
if (subs && size(subs)) {
177+
const value = this.get();
178+
if (subs.lastVersion_ !== this.version_) {
179+
subs.lastVersion_ = this.version_;
180+
send(subs, value);
170181
}
171182
}
172183
}
@@ -190,8 +201,8 @@ export class ReadableImpl<TValue = any> implements BatchTask {
190201
} else {
191202
this.disposed_ = true;
192203
}
193-
tasks.delete(this);
194-
this.dependents_ = undefined;
204+
batchTasks.delete(this);
205+
this.dependents_ = this.subs_ = undefined;
195206
if (this.deps_) {
196207
registry.unregister(this.deps_);
197208
if (this.weakRefSelf_) {
@@ -264,12 +275,12 @@ export class ReadableImpl<TValue = any> implements BatchTask {
264275

265276
const isFirst = batchStart();
266277

267-
tasks.add(this);
278+
batchTasks.add(this);
268279

269280
if (this.dependents_) {
270281
for (const ref of this.dependents_) {
271282
const dependent = ref.deref();
272-
if (dependent && !tasks.has(dependent)) {
283+
if (dependent && !batchTasks.has(dependent)) {
273284
dependent.notify_();
274285
}
275286
}
@@ -280,24 +291,9 @@ export class ReadableImpl<TValue = any> implements BatchTask {
280291

281292
/** @internal */
282293
public onReaction_(subscriber: Subscriber<TValue>, scheduler: Scheduler = SyncScheduler): void {
283-
let subs = this.subs?.get(scheduler);
294+
let subs = this.subs_?.get(scheduler);
284295
if (!subs) {
285-
(this.subs ??= new Map()).set(
286-
scheduler,
287-
(subs = {
288-
lastVersion_: this.version,
289-
schedule_: ((scheduler: Scheduler) => {
290-
const subs = this.subs?.get(scheduler);
291-
if (subs && size(subs)) {
292-
const value = this.get();
293-
if (subs.lastVersion_ !== this.version_) {
294-
subs.lastVersion_ = this.version_;
295-
send(subs, value);
296-
}
297-
}
298-
}).bind(0, scheduler),
299-
}),
300-
);
296+
(this.subs_ ??= new Map()).set(scheduler, (subs = { lastVersion_: this.version }));
301297
} else if (!size(subs)) {
302298
// start tracking last first on first subscription
303299
subs.lastVersion_ = this.version;
@@ -351,18 +347,18 @@ export class ReadableImpl<TValue = any> implements BatchTask {
351347
}
352348

353349
public unsubscribe(subscriber?: (...args: any[]) => any, scheduler?: Scheduler): void {
354-
if (this.subs) {
350+
if (this.subs_) {
355351
if (subscriber) {
356352
if (scheduler) {
357-
const subs = this.subs.get(scheduler);
353+
const subs = this.subs_.get(scheduler);
358354
subs && off(subs, subscriber);
359355
} else {
360-
for (const subs of this.subs.values()) {
356+
for (const subs of this.subs_.values()) {
361357
off(subs, subscriber);
362358
}
363359
}
364360
} else {
365-
this.subs.clear();
361+
this.subs_.clear();
366362
}
367363
}
368364
}

src/schedulers/AsyncScheduler.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { type Scheduler, type SchedulerFlush, type SchedulerTask } from "./interface";
2+
3+
/**
4+
* Creates an async {@link Scheduler}.
5+
* @param defer - A function that defers the execution of the {@link SchedulerFlush} function.
6+
* @returns- A {@link Scheduler}
7+
*
8+
* @example
9+
* ```ts
10+
* import { asyncScheduler } from "@embra/reactivity";
11+
* const MicroTaskScheduler = asyncScheduler(flush => Promise.resolve().then(flush));
12+
* const AnimationFrameScheduler = asyncScheduler(requestAnimationFrame);
13+
* ```
14+
*/
15+
export const asyncScheduler = (defer: (flush: SchedulerFlush) => unknown): Scheduler => {
16+
let tasks: Set<SchedulerTask>;
17+
let pending: boolean | undefined;
18+
const flush = (): void => {
19+
for (const task of tasks) {
20+
tasks.delete(task);
21+
try {
22+
task.schedulerTask_(AsyncScheduler);
23+
} catch (e) {
24+
console.error(e);
25+
}
26+
}
27+
pending = false;
28+
};
29+
30+
const AsyncScheduler: Scheduler = (task: SchedulerTask): void => {
31+
(tasks ??= new Set()).add(task);
32+
pending ||= (defer(flush), true);
33+
};
34+
35+
return AsyncScheduler;
36+
};

0 commit comments

Comments
 (0)