bug/feat: use a better implementation of 'asap scheduled' (and buffered per event-cycle) events

This commit is contained in:
mathuo 2024-05-03 20:06:43 +01:00
parent d0eda81a9e
commit ffe7d59b74
No known key found for this signature in database
GPG Key ID: C6EEDEFD6CA07281
2 changed files with 44 additions and 10 deletions

View File

@ -203,19 +203,53 @@ export function addDisposableListener<K extends keyof HTMLElementEventMap>(
};
}
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<void>();
readonly onEvent = this._onFired.event;
private _currentFireCount = 0;
private _queued = false;
readonly onEvent: Event<void> = (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);
});
}

View File

@ -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<T extends IGridPanelView>
readonly onDidActiveChange: Event<T | undefined> =
this._onDidActiveChange.event;
protected readonly _bufferOnDidLayoutChange = new TickDelayedEvent();
protected readonly _bufferOnDidLayoutChange = new AsapEvent();
get id(): string {
return this._id;