Path: blob/main/extensions/microsoft-authentication/src/common/event.ts
3320 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/4import { Event } from 'vscode';56/**7* The EventBufferer is useful in situations in which you want8* to delay firing your events during some code.9* You can wrap that code and be sure that the event will not10* be fired during that wrap.11*12* ```13* const emitter: Emitter;14* const delayer = new EventDelayer();15* const delayedEvent = delayer.wrapEvent(emitter.event);16*17* delayedEvent(console.log);18*19* delayer.bufferEvents(() => {20* emitter.fire(); // event will not be fired yet21* });22*23* // event will only be fired at this point24* ```25*/26export class EventBufferer {2728private data: { buffers: Function[] }[] = [];2930wrapEvent<T>(event: Event<T>): Event<T>;31wrapEvent<T>(event: Event<T>, reduce: (last: T | undefined, event: T) => T): Event<T>;32wrapEvent<T, O>(event: Event<T>, reduce: (last: O | undefined, event: T) => O, initial: O): Event<O>;33wrapEvent<T, O>(event: Event<T>, reduce?: (last: T | O | undefined, event: T) => T | O, initial?: O): Event<O | T> {34return (listener, thisArgs?, disposables?) => {35return event(i => {36const data = this.data[this.data.length - 1];3738// Non-reduce scenario39if (!reduce) {40// Buffering case41if (data) {42data.buffers.push(() => listener.call(thisArgs, i));43} else {44// Not buffering case45listener.call(thisArgs, i);46}47return;48}4950// Reduce scenario51const reduceData = data as typeof data & {52/**53* The accumulated items that will be reduced.54*/55items?: T[];56/**57* The reduced result cached to be shared with other listeners.58*/59reducedResult?: T | O;60};6162// Not buffering case63if (!reduceData) {64// TODO: Is there a way to cache this reduce call for all listeners?65listener.call(thisArgs, reduce(initial, i));66return;67}6869// Buffering case70reduceData.items ??= [];71reduceData.items.push(i);72if (reduceData.buffers.length === 0) {73// Include a single buffered function that will reduce all events when we're done buffering events74data.buffers.push(() => {75// cache the reduced result so that the value can be shared across all listeners76reduceData.reducedResult ??= initial77? reduceData.items!.reduce(reduce as (last: O | undefined, event: T) => O, initial)78: reduceData.items!.reduce(reduce as (last: T | undefined, event: T) => T);79listener.call(thisArgs, reduceData.reducedResult);80});81}82}, undefined, disposables);83};84}8586bufferEvents<R = void>(fn: () => R): R {87const data = { buffers: new Array<Function>() };88this.data.push(data);89const r = fn();90this.data.pop();91data.buffers.forEach(flush => flush());92return r;93}9495async bufferEventsAsync<R = void>(fn: () => Promise<R>): Promise<R> {96const data = { buffers: new Array<Function>() };97this.data.push(data);98try {99const r = await fn();100return r;101} finally {102this.data.pop();103data.buffers.forEach(flush => flush());104}105}106}107108109