Skip to content

Commit

Permalink
feat: new export getScheduler
Browse files Browse the repository at this point in the history
  • Loading branch information
mihar-22 committed Jul 6, 2022
1 parent 24a4789 commit f92183b
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 6 deletions.
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ $: yarn add @maverick-js/observables
- [`onDispose`](#ondispose)
- [`isObservable`](#isobservable)
- [`isSubject`](#issubject)
- [`getScheduler`](#getscheduler)

## `$root`

Expand Down Expand Up @@ -391,6 +392,26 @@ isSubject($computed(() => 10));
isSubject($readonly($observable(10)));
```

### `getScheduler`

Returns the global scheduler which can be used to queue additional tasks or synchronously flush
the queue.

```js
const scheduler = getScheduler();

// Queue task
scheduler.enqueue(() => {
// ...
});

// Flush queue synchronously
scheduler.syncFlush();
```

> **Note**
> See the ['Scheduler'](#scheduler) section for more information.
## Debugging

The `$observable`, `$computed`, and `$effect` functions accept a debugging ID (string) as part
Expand Down
9 changes: 8 additions & 1 deletion src/observables.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createScheduler } from './scheduler';
import { createScheduler, type Scheduler } from './scheduler';

export type Observable<T> = {
id?: string;
Expand Down Expand Up @@ -377,6 +377,13 @@ export function isSubject<T>(fn: MaybeObservable<T>): fn is ObservableSubject<T>
return isObservable(fn) && !!(fn as ObservableSubject<T>).set;
}

/**
* Returns the global scheduler.
*/
export function getScheduler(): Scheduler {
return _scheduler;
}

type Node = {
id?: string;
(): any;
Expand Down
12 changes: 7 additions & 5 deletions src/scheduler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export type Scheduler = {
enqueue: (task: ScheduledTask) => void;
served: (task: ScheduledTask) => boolean;
flush: () => void;
syncFlush: () => void;
tick: Promise<void>;
};

Expand All @@ -29,14 +30,14 @@ export type Scheduler = {
* ```
*/
export function createScheduler(onFlush?: SchedulerFlushed): Scheduler {
const processed = new Set<ScheduledTask>();
const served = new Set<ScheduledTask>();
const queue = new Set<ScheduledTask>();
const microtask = Promise.resolve();
const queueTask = typeof queueMicrotask !== 'undefined' ? queueMicrotask : microtask.then;

function enqueue(task: ScheduledTask) {
// `processed` is only populated during a flush.
if (!processed.has(task)) queue.add(task);
if (!served.has(task)) queue.add(task);
scheduleFlush();
}

Expand All @@ -52,20 +53,21 @@ export function createScheduler(onFlush?: SchedulerFlushed): Scheduler {
do {
for (const task of queue) {
task();
processed.add(task);
served.add(task);
queue.delete(task);
}
} while (queue.size > 0);

processed.clear();
served.clear();
flushing = false;
onFlush?.();
}

return {
enqueue,
served: (task) => processed.has(task),
served: (task) => served.has(task),
flush: scheduleFlush,
syncFlush: flush,
tick: microtask,
};
}
29 changes: 29 additions & 0 deletions tests/syncFlush.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { $effect, $observable, getScheduler } from '../src';

it('should flush queue synchronously', async () => {
const scheduler = getScheduler();

const $a = $observable(0);
const $b = $observable(0);

const effect = vi.fn();
$effect(() => {
$a(), $b(), effect();
});

$a.set(2);
$b.set(3);

scheduler.syncFlush();
expect($a()).toBe(2);
expect($b()).toBe(3);
expect(effect).toHaveBeenCalledTimes(2);

$a.set(4);
$b.set(6);

scheduler.syncFlush();
expect($a()).toBe(4);
expect($b()).toBe(6);
expect(effect).toHaveBeenCalledTimes(3);
});

0 comments on commit f92183b

Please sign in to comment.