Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/microsoft-authentication/src/common/event.ts
3320 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
import { Event } from 'vscode';
6
7
/**
8
* The EventBufferer is useful in situations in which you want
9
* to delay firing your events during some code.
10
* You can wrap that code and be sure that the event will not
11
* be fired during that wrap.
12
*
13
* ```
14
* const emitter: Emitter;
15
* const delayer = new EventDelayer();
16
* const delayedEvent = delayer.wrapEvent(emitter.event);
17
*
18
* delayedEvent(console.log);
19
*
20
* delayer.bufferEvents(() => {
21
* emitter.fire(); // event will not be fired yet
22
* });
23
*
24
* // event will only be fired at this point
25
* ```
26
*/
27
export class EventBufferer {
28
29
private data: { buffers: Function[] }[] = [];
30
31
wrapEvent<T>(event: Event<T>): Event<T>;
32
wrapEvent<T>(event: Event<T>, reduce: (last: T | undefined, event: T) => T): Event<T>;
33
wrapEvent<T, O>(event: Event<T>, reduce: (last: O | undefined, event: T) => O, initial: O): Event<O>;
34
wrapEvent<T, O>(event: Event<T>, reduce?: (last: T | O | undefined, event: T) => T | O, initial?: O): Event<O | T> {
35
return (listener, thisArgs?, disposables?) => {
36
return event(i => {
37
const data = this.data[this.data.length - 1];
38
39
// Non-reduce scenario
40
if (!reduce) {
41
// Buffering case
42
if (data) {
43
data.buffers.push(() => listener.call(thisArgs, i));
44
} else {
45
// Not buffering case
46
listener.call(thisArgs, i);
47
}
48
return;
49
}
50
51
// Reduce scenario
52
const reduceData = data as typeof data & {
53
/**
54
* The accumulated items that will be reduced.
55
*/
56
items?: T[];
57
/**
58
* The reduced result cached to be shared with other listeners.
59
*/
60
reducedResult?: T | O;
61
};
62
63
// Not buffering case
64
if (!reduceData) {
65
// TODO: Is there a way to cache this reduce call for all listeners?
66
listener.call(thisArgs, reduce(initial, i));
67
return;
68
}
69
70
// Buffering case
71
reduceData.items ??= [];
72
reduceData.items.push(i);
73
if (reduceData.buffers.length === 0) {
74
// Include a single buffered function that will reduce all events when we're done buffering events
75
data.buffers.push(() => {
76
// cache the reduced result so that the value can be shared across all listeners
77
reduceData.reducedResult ??= initial
78
? reduceData.items!.reduce(reduce as (last: O | undefined, event: T) => O, initial)
79
: reduceData.items!.reduce(reduce as (last: T | undefined, event: T) => T);
80
listener.call(thisArgs, reduceData.reducedResult);
81
});
82
}
83
}, undefined, disposables);
84
};
85
}
86
87
bufferEvents<R = void>(fn: () => R): R {
88
const data = { buffers: new Array<Function>() };
89
this.data.push(data);
90
const r = fn();
91
this.data.pop();
92
data.buffers.forEach(flush => flush());
93
return r;
94
}
95
96
async bufferEventsAsync<R = void>(fn: () => Promise<R>): Promise<R> {
97
const data = { buffers: new Array<Function>() };
98
this.data.push(data);
99
try {
100
const r = await fn();
101
return r;
102
} finally {
103
this.data.pop();
104
data.buffers.forEach(flush => flush());
105
}
106
}
107
}
108
109