From ffe7d59b745b20411e953f1fcaa1072ff81c509d Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Fri, 3 May 2024 20:06:43 +0100 Subject: [PATCH] bug/feat: use a better implementation of 'asap scheduled' (and buffered per event-cycle) events --- packages/dockview-core/src/events.ts | 50 ++++++++++++++++--- .../src/gridview/baseComponentGridview.ts | 4 +- 2 files changed, 44 insertions(+), 10 deletions(-) diff --git a/packages/dockview-core/src/events.ts b/packages/dockview-core/src/events.ts index 160a3b96c..8916543c5 100644 --- a/packages/dockview-core/src/events.ts +++ b/packages/dockview-core/src/events.ts @@ -203,19 +203,53 @@ export function addDisposableListener( }; } -export class TickDelayedEvent implements IDisposable { - private timer: any; - +/** + * + * Event Emitter that fires events from a Microtask callback, only one event will fire per event-loop cycle. + * + * It's kind of like using an `asapScheduler` in RxJs with additional logic to only fire once per event-loop cycle. + * This implementation exists to avoid external dependencies. + * + * @see https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask + * @see https://rxjs.dev/api/index/const/asapScheduler + */ +export class AsapEvent implements IDisposable { private readonly _onFired = new Emitter(); - readonly onEvent = this._onFired.event; + private _currentFireCount = 0; + private _queued = false; + + readonly onEvent: Event = (e) => { + /** + * when the event is first subscribed to take note of the current fire count + */ + const fireCountAtTimeOfEventSubscription = this._currentFireCount; + + return this._onFired.event(() => { + /** + * if the current fire count is greater than the fire count at event subscription + * then the event has been fired since we subscribed and it's ok to "on_next" the event. + * + * if the count is not greater then what we are recieving is an event from the microtask + * queue that was triggered before we actually subscribed and therfore we should ignore it. + */ + if (this._currentFireCount > fireCountAtTimeOfEventSubscription) { + e(); + } + }); + }; fire(): void { - if (this.timer) { - clearTimeout(this.timer); + this._currentFireCount++; + + if (this._queued) { + return; } - this.timer = setTimeout(() => { + + this._queued = true; + + queueMicrotask(() => { + this._queued = false; this._onFired.fire(); - clearTimeout(this.timer); }); } diff --git a/packages/dockview-core/src/gridview/baseComponentGridview.ts b/packages/dockview-core/src/gridview/baseComponentGridview.ts index 3150a4c81..e6e572786 100644 --- a/packages/dockview-core/src/gridview/baseComponentGridview.ts +++ b/packages/dockview-core/src/gridview/baseComponentGridview.ts @@ -1,4 +1,4 @@ -import { Emitter, Event, TickDelayedEvent } from '../events'; +import { Emitter, Event, AsapEvent } from '../events'; import { getGridLocation, Gridview, IGridView } from './gridview'; import { Position } from '../dnd/droptarget'; import { Disposable, IValueDisposable } from '../lifecycle'; @@ -92,7 +92,7 @@ export abstract class BaseGrid readonly onDidActiveChange: Event = this._onDidActiveChange.event; - protected readonly _bufferOnDidLayoutChange = new TickDelayedEvent(); + protected readonly _bufferOnDidLayoutChange = new AsapEvent(); get id(): string { return this._id;