forked from microsoft/vscode
-
Notifications
You must be signed in to change notification settings - Fork 450
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
validate vscode.extensions in .gitpod.yml
- Loading branch information
Showing
16 changed files
with
538 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. See License.txt in the project root for license information. | ||
*--------------------------------------------------------------------------------------------*/ | ||
|
||
export interface ITask<T> { | ||
(): T; | ||
} | ||
|
||
/** | ||
* A helper to prevent accumulation of sequential async tasks. | ||
* | ||
* Imagine a mail man with the sole task of delivering letters. As soon as | ||
* a letter submitted for delivery, he drives to the destination, delivers it | ||
* and returns to his base. Imagine that during the trip, N more letters were submitted. | ||
* When the mail man returns, he picks those N letters and delivers them all in a | ||
* single trip. Even though N+1 submissions occurred, only 2 deliveries were made. | ||
* | ||
* The throttler implements this via the queue() method, by providing it a task | ||
* factory. Following the example: | ||
* | ||
* var throttler = new Throttler(); | ||
* var letters = []; | ||
* | ||
* function letterReceived(l) { | ||
* letters.push(l); | ||
* throttler.queue(() => { return makeTheTrip(); }); | ||
* } | ||
*/ | ||
export class Throttler<T> { | ||
|
||
private activePromise: Promise<T> | null; | ||
private queuedPromise: Promise<T> | null; | ||
private queuedPromiseFactory: ITask<Promise<T>> | null; | ||
|
||
constructor() { | ||
this.activePromise = null; | ||
this.queuedPromise = null; | ||
this.queuedPromiseFactory = null; | ||
} | ||
|
||
public queue(promiseFactory: ITask<Promise<T>>): Promise<T> { | ||
if (this.activePromise) { | ||
this.queuedPromiseFactory = promiseFactory; | ||
|
||
if (!this.queuedPromise) { | ||
let onComplete = () => { | ||
this.queuedPromise = null; | ||
|
||
let result = this.queue(this.queuedPromiseFactory!); | ||
this.queuedPromiseFactory = null; | ||
|
||
return result; | ||
}; | ||
|
||
this.queuedPromise = new Promise<T>((resolve) => { | ||
this.activePromise!.then(onComplete, onComplete).then(resolve); | ||
}); | ||
} | ||
|
||
return new Promise<T>((resolve, reject) => { | ||
this.queuedPromise!.then(resolve, reject); | ||
}); | ||
} | ||
|
||
this.activePromise = promiseFactory(); | ||
|
||
return new Promise<T>((resolve, reject) => { | ||
this.activePromise!.then((result: T) => { | ||
this.activePromise = null; | ||
resolve(result); | ||
}, (err: any) => { | ||
this.activePromise = null; | ||
reject(err); | ||
}); | ||
}); | ||
} | ||
} | ||
|
||
/** | ||
* A helper to delay execution of a task that is being requested often. | ||
* | ||
* Following the throttler, now imagine the mail man wants to optimize the number of | ||
* trips proactively. The trip itself can be long, so the he decides not to make the trip | ||
* as soon as a letter is submitted. Instead he waits a while, in case more | ||
* letters are submitted. After said waiting period, if no letters were submitted, he | ||
* decides to make the trip. Imagine that N more letters were submitted after the first | ||
* one, all within a short period of time between each other. Even though N+1 | ||
* submissions occurred, only 1 delivery was made. | ||
* | ||
* The delayer offers this behavior via the trigger() method, into which both the task | ||
* to be executed and the waiting period (delay) must be passed in as arguments. Following | ||
* the example: | ||
* | ||
* var delayer = new Delayer(WAITING_PERIOD); | ||
* var letters = []; | ||
* | ||
* function letterReceived(l) { | ||
* letters.push(l); | ||
* delayer.trigger(() => { return makeTheTrip(); }); | ||
* } | ||
*/ | ||
export class Delayer<T> { | ||
|
||
public defaultDelay: number; | ||
private timeout: NodeJS.Timer | null; | ||
private completionPromise: Promise<T> | null; | ||
private onResolve: ((value: T | PromiseLike<T> | undefined) => void) | null; | ||
private task: ITask<T> | null; | ||
|
||
constructor(defaultDelay: number) { | ||
this.defaultDelay = defaultDelay; | ||
this.timeout = null; | ||
this.completionPromise = null; | ||
this.onResolve = null; | ||
this.task = null; | ||
} | ||
|
||
public trigger(task: ITask<T>, delay: number = this.defaultDelay): Promise<T> { | ||
this.task = task; | ||
this.cancelTimeout(); | ||
|
||
if (!this.completionPromise) { | ||
this.completionPromise = new Promise<T | undefined>((resolve) => { | ||
this.onResolve = resolve; | ||
}).then(() => { | ||
this.completionPromise = null; | ||
this.onResolve = null; | ||
|
||
let result = this.task!(); | ||
this.task = null; | ||
|
||
return result; | ||
}); | ||
} | ||
|
||
this.timeout = setTimeout(() => { | ||
this.timeout = null; | ||
this.onResolve!(undefined); | ||
}, delay); | ||
|
||
return this.completionPromise; | ||
} | ||
|
||
public isTriggered(): boolean { | ||
return this.timeout !== null; | ||
} | ||
|
||
public cancel(): void { | ||
this.cancelTimeout(); | ||
|
||
if (this.completionPromise) { | ||
this.completionPromise = null; | ||
} | ||
} | ||
|
||
private cancelTimeout(): void { | ||
if (this.timeout !== null) { | ||
clearTimeout(this.timeout); | ||
this.timeout = null; | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* A helper to delay execution of a task that is being requested often, while | ||
* preventing accumulation of consecutive executions, while the task runs. | ||
* | ||
* Simply combine the two mail man strategies from the Throttler and Delayer | ||
* helpers, for an analogy. | ||
*/ | ||
export class ThrottledDelayer<T> extends Delayer<Promise<T>> { | ||
|
||
private throttler: Throttler<T>; | ||
|
||
constructor(defaultDelay: number) { | ||
super(defaultDelay); | ||
|
||
this.throttler = new Throttler<T>(); | ||
} | ||
|
||
public override trigger(promiseFactory: ITask<Promise<T>>, delay?: number): Promise<Promise<T>> { | ||
return super.trigger(() => this.throttler.queue(promiseFactory), delay); | ||
} | ||
} |
Oops, something went wrong.