[REF] utils: move batched from reactivity to utils

This commit is contained in:
Géry Debongnie
2022-01-10 13:46:11 +01:00
committed by Aaron Bohy
parent b33471e819
commit 6d9ed0d62f
4 changed files with 48 additions and 27 deletions
+1 -25
View File
@@ -1,5 +1,6 @@
import { onWillUnmount } from "./component/lifecycle_hooks";
import { ComponentNode, getCurrent } from "./component/component_node";
import { batched, Callback } from "./utils";
// Allows to get the target of a Reactive (used for making a new Reactive from the underlying object)
const TARGET = Symbol("Target");
@@ -8,7 +9,6 @@ const KEYCHANGES = Symbol("Key changes");
type ObjectKey = string | number | symbol;
type Target = object;
type Callback = () => void;
export type Reactive<T extends Target = Target> = T & {
[TARGET]: any;
};
@@ -191,30 +191,6 @@ export function reactive<T extends Target>(target: T, callback: Callback = () =>
return reactivesForTarget.get(callback) as Reactive<T>;
}
/**
* Creates a batched version of a callback so that all calls to it in the same
* microtick will only call the original callback once.
*
* @param callback the callback to batch
* @returns a batched version of the original callback
*/
export function batched(callback: Callback): Callback {
let called = false;
return async () => {
// This await blocks all calls to the callback here, then releases them sequentially
// in the next microtick. This line decides the granularity of the batch.
await Promise.resolve();
if (!called) {
called = true;
callback();
// wait for all calls in this microtick to fall through before resetting "called"
// so that only the first call to the batched function calls the original callback
await Promise.resolve();
called = false;
}
};
}
const batchedRenderFunctions = new WeakMap<ComponentNode, Callback>();
/**
* Creates a reactive object that will be observed by the current component.
+26
View File
@@ -1,3 +1,29 @@
export type Callback = () => void;
/**
* Creates a batched version of a callback so that all calls to it in the same
* microtick will only call the original callback once.
*
* @param callback the callback to batch
* @returns a batched version of the original callback
*/
export function batched(callback: Callback): Callback {
let called = false;
return async () => {
// This await blocks all calls to the callback here, then releases them sequentially
// in the next microtick. This line decides the granularity of the batch.
await Promise.resolve();
if (!called) {
called = true;
callback();
// wait for all calls in this microtick to fall through before resetting "called"
// so that only the first call to the batched function calls the original callback
await Promise.resolve();
called = false;
}
};
}
export class EventBus extends EventTarget {
trigger(name: string, payload?: any) {
this.dispatchEvent(new CustomEvent(name, { detail: payload }));
+2 -1
View File
@@ -7,7 +7,8 @@ import {
useState,
xml,
} from "../src";
import { batched, reactive } from "../src/reactivity";
import { reactive } from "../src/reactivity";
import { batched } from "../src/utils";
import {
makeDeferred,
makeTestFixture,
+19 -1
View File
@@ -1,4 +1,5 @@
import { EventBus } from "../src/utils";
import { batched, EventBus } from "../src/utils";
import { nextMicroTick } from "./helpers";
describe("event bus behaviour", () => {
test("can subscribe and be notified", () => {
@@ -33,3 +34,20 @@ describe("event bus behaviour", () => {
bus.trigger("event", "hello world");
});
});
describe("batched", () => {
test("callback is called only once after operations", async () => {
let n = 0;
let fn = batched(() => n++);
expect(n).toBe(0);
fn();
fn();
expect(n).toBe(0);
await nextMicroTick();
expect(n).toBe(1);
await nextMicroTick();
expect(n).toBe(1);
});
});