forked from DonJayamanne/pythonVSCode
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcancellation.ts
More file actions
135 lines (124 loc) · 4.36 KB
/
cancellation.ts
File metadata and controls
135 lines (124 loc) · 4.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
'use strict';
import { CancellationToken, CancellationTokenSource } from 'vscode';
import { createDeferred } from './utils/async';
import * as localize from './utils/localize';
/**
* Error type thrown when canceling.
*/
export class CancellationError extends Error {
constructor() {
super(localize.Common.canceled);
}
}
/**
* Create a promise that will either resolve with a default value or reject when the token is cancelled.
*
* @export
* @template T
* @param {({ defaultValue: T; token: CancellationToken; cancelAction: 'reject' | 'resolve' })} options
* @returns {Promise<T>}
*/
export function createPromiseFromCancellation<T>(options: {
defaultValue: T;
token?: CancellationToken;
cancelAction: 'reject' | 'resolve';
}): Promise<T> {
return new Promise<T>((resolve, reject) => {
// Never resolve.
if (!options.token) {
return;
}
const complete = () => {
const optionsToken = options.token!; // NOSONAR
if (optionsToken.isCancellationRequested) {
if (options.cancelAction === 'resolve') {
return resolve(options.defaultValue);
}
if (options.cancelAction === 'reject') {
return reject(new CancellationError());
}
}
};
options.token.onCancellationRequested(complete);
});
}
/**
* Create a single unified cancellation token that wraps multiple cancellation tokens.
*
* @export
* @param {(...(CancellationToken | undefined)[])} tokens
* @returns {CancellationToken}
*/
export function wrapCancellationTokens(...tokens: (CancellationToken | undefined)[]): CancellationToken {
const wrappedCancellantionToken = new CancellationTokenSource();
for (const token of tokens) {
if (!token) {
continue;
}
if (token.isCancellationRequested) {
return token;
}
token.onCancellationRequested(() => wrappedCancellantionToken.cancel());
}
return wrappedCancellantionToken.token;
}
export namespace Cancellation {
/**
* Races a promise and cancellation. Promise can take a cancellation token too in order to listen to cancellation.
* @param work function returning a promise to race
* @param token token used for cancellation
*/
export function race<T>(work: (token?: CancellationToken) => Promise<T>, token?: CancellationToken): Promise<T> {
if (token) {
// Use a deferred promise. Resolves when the work finishes
const deferred = createDeferred<T>();
// Cancel the deferred promise when the cancellation happens
token.onCancellationRequested(() => {
if (!deferred.completed) {
deferred.reject(new CancellationError());
}
});
// Might already be canceled
if (token.isCancellationRequested) {
// Just start out as rejected
deferred.reject(new CancellationError());
} else {
// Not canceled yet. When the work finishes
// either resolve our promise or cancel.
work(token)
.then((v) => {
if (!deferred.completed) {
deferred.resolve(v);
}
})
.catch((e) => {
if (!deferred.completed) {
deferred.reject(e);
}
});
}
return deferred.promise;
} else {
// No actual token, just do the original work.
return work();
}
}
/**
* isCanceled returns a boolean indicating if the cancel token has been canceled.
* @param cancelToken
*/
export function isCanceled(cancelToken?: CancellationToken): boolean {
return cancelToken ? cancelToken.isCancellationRequested : false;
}
/**
* throws a CancellationError if the token is canceled.
* @param cancelToken
*/
export function throwIfCanceled(cancelToken?: CancellationToken): void {
if (isCanceled(cancelToken)) {
throw new CancellationError();
}
}
}