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