Merge from vscode 4c9161a3f125f5a788aafa73a4ecfcb27dcfc319 (#9143)

* Merge from vscode 4c9161a3f125f5a788aafa73a4ecfcb27dcfc319

* minor spacing fix
This commit is contained in:
Anthony Dresser
2020-02-13 21:20:36 -06:00
committed by GitHub
parent 0c56d44e4f
commit 71375808b9
57 changed files with 706 additions and 510 deletions

View File

@@ -779,3 +779,107 @@ export async function retry<T>(task: ITask<Promise<T>>, delay: number, retries:
throw lastError;
}
//#region Task Sequentializer
interface IPendingTask {
taskId: number;
cancel: () => void;
promise: Promise<void>;
}
interface ISequentialTask {
promise: Promise<void>;
promiseResolve: () => void;
promiseReject: (error: Error) => void;
run: () => Promise<void>;
}
export interface ITaskSequentializerWithPendingTask {
readonly pending: Promise<void>;
}
export class TaskSequentializer {
private _pending?: IPendingTask;
private _next?: ISequentialTask;
hasPending(taskId?: number): this is ITaskSequentializerWithPendingTask {
if (!this._pending) {
return false;
}
if (typeof taskId === 'number') {
return this._pending.taskId === taskId;
}
return !!this._pending;
}
get pending(): Promise<void> | undefined {
return this._pending ? this._pending.promise : undefined;
}
cancelPending(): void {
this._pending?.cancel();
}
setPending(taskId: number, promise: Promise<void>, onCancel?: () => void, ): Promise<void> {
this._pending = { taskId: taskId, cancel: () => onCancel?.(), promise };
promise.then(() => this.donePending(taskId), () => this.donePending(taskId));
return promise;
}
private donePending(taskId: number): void {
if (this._pending && taskId === this._pending.taskId) {
// only set pending to done if the promise finished that is associated with that taskId
this._pending = undefined;
// schedule the next task now that we are free if we have any
this.triggerNext();
}
}
private triggerNext(): void {
if (this._next) {
const next = this._next;
this._next = undefined;
// Run next task and complete on the associated promise
next.run().then(next.promiseResolve, next.promiseReject);
}
}
setNext(run: () => Promise<void>): Promise<void> {
// this is our first next task, so we create associated promise with it
// so that we can return a promise that completes when the task has
// completed.
if (!this._next) {
let promiseResolve: () => void;
let promiseReject: (error: Error) => void;
const promise = new Promise<void>((resolve, reject) => {
promiseResolve = resolve;
promiseReject = reject;
});
this._next = {
run,
promise,
promiseResolve: promiseResolve!,
promiseReject: promiseReject!
};
}
// we have a previous next task, just overwrite it
else {
this._next.run = run;
}
return this._next.promise;
}
}
//#endregion