microsoft/vscode-react-native

Public

mirrored fromhttps://github.com/microsoft/vscode-react-nativeAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
dev/v-peq/add-expo-packager-command-tests

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

src/common/node/promise.ts

235lines · modecode

1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for details.
3
4import { CancellationTokenSource, Disposable } from "vscode";
5
6/**
7 * Utilities for working with promises.
8 */
9export class PromiseUtil {
10 public static async forEach<T>(
11 sources: T[],
12 promiseGenerator: (source: T) => Promise<void>,
13 ): Promise<void> {
14 await Promise.all(sources.map(source => promiseGenerator(source)));
15 }
16 /**
17 * Retries an operation a given number of times. For each retry, a condition is checked.
18 * If the condition is not satisfied after the maximum number of retries, and error is thrown.
19 * Otherwise, the result of the operation is returned once the condition is satisfied.
20 *
21 * @param operation - the function to execute.
22 * @param condition - the condition to check between iterations.
23 * @param maxRetries - the maximum number of retries.
24 * @param delay - time between iterations, in milliseconds.
25 * @param failure - error description.
26 */
27 public static retryAsync<T>(
28 operation: () => Promise<T>,
29 condition: (result: T) => boolean | Promise<boolean>,
30 maxRetries: number,
31 delay: number,
32 failure: string,
33 cancellationTokenSource?: CancellationTokenSource,
34 ): Promise<T> {
35 return this.retryAsyncIteration(
36 operation,
37 condition,
38 maxRetries,
39 0,
40 delay,
41 failure,
42 cancellationTokenSource,
43 );
44 }
45
46 public static async reduce<T>(
47 sources: T[] | Promise<T[]>,
48 generateAsyncOperation: (value: T) => Promise<void>,
49 ): Promise<void> {
50 const arraySources: T[] = sources instanceof Promise ? await sources : sources;
51
52 return arraySources.reduce(async (previousReduction: Promise<void>, newSource: T) => {
53 await previousReduction;
54 return generateAsyncOperation(newSource);
55 }, Promise.resolve());
56 }
57
58 public static async delay(duration: number): Promise<void> {
59 return new Promise<void>(resolve => {
60 setTimeout(resolve, duration);
61 });
62 }
63
64 public static waitUntil<T>(
65 condition: () => Promise<T | null> | T | null,
66 interval: number = 1000,
67 timeout?: number,
68 ): Promise<T | null> {
69 return new Promise(async (resolve, reject) => {
70 let rejectTimeout: NodeJS.Timeout | undefined;
71 // eslint-disable-next-line prefer-const
72 let сheckInterval: NodeJS.Timeout | undefined;
73
74 if (timeout) {
75 rejectTimeout = setTimeout(() => {
76 // eslint-disable-next-line @typescript-eslint/no-use-before-define
77 cleanup();
78 resolve(null);
79 }, timeout);
80 }
81
82 const cleanup = () => {
83 if (rejectTimeout) {
84 clearTimeout(rejectTimeout);
85 }
86 if (сheckInterval) {
87 clearInterval(сheckInterval);
88 }
89 };
90
91 const tryToResolve = async (): Promise<boolean> => {
92 try {
93 const result = await condition();
94 if (result) {
95 cleanup();
96 resolve(result);
97 }
98 return !!result;
99 } catch (err) {
100 cleanup();
101 reject(err);
102 return false;
103 }
104 };
105
106 const resolved = await tryToResolve();
107 if (resolved) {
108 return;
109 }
110
111 сheckInterval = setInterval(async () => {
112 await tryToResolve();
113 }, interval);
114 });
115 }
116
117 public static promiseCacheDecorator<T>(
118 func: (...args: any[]) => Promise<T>,
119 context: Record<string, any> | null = null,
120 ): (...args: any[]) => Promise<T> {
121 let promise: Promise<T> | undefined;
122 return (...args: any[]): Promise<T> => {
123 if (!promise) {
124 promise = func.apply(context, args) as Promise<T>;
125 }
126 return promise;
127 };
128 }
129
130 private static async retryAsyncIteration<T>(
131 operation: () => Promise<T>,
132 condition: (result: T) => boolean | Promise<boolean>,
133 maxRetries: number,
134 iteration: number,
135 delay: number,
136 failure: string,
137 cancellationTokenSource?: CancellationTokenSource,
138 ): Promise<T> {
139 const result = await operation();
140 const conditionResult = await condition(result);
141 if (conditionResult) {
142 return result;
143 }
144
145 if (
146 iteration < maxRetries &&
147 !(cancellationTokenSource && cancellationTokenSource.token.isCancellationRequested)
148 ) {
149 await PromiseUtil.delay(delay);
150 return this.retryAsyncIteration(
151 operation,
152 condition,
153 maxRetries,
154 iteration + 1,
155 delay,
156 failure,
157 cancellationTokenSource,
158 );
159 }
160
161 throw new Error(failure);
162 }
163}
164
165export class Delayer<T> implements Disposable {
166 private timeout: any;
167 private completionPromise: Promise<any> | null;
168 private doResolve: ((value?: any | Promise<any>) => void) | null;
169 private doReject: ((err: any) => void) | null;
170 private task: { (): T | Promise<T> } | null;
171
172 constructor() {
173 this.timeout = null;
174 this.completionPromise = null;
175 this.doResolve = null;
176 this.doReject = null;
177 this.task = null;
178 }
179
180 public runWihtDelay(task: { (): T | Promise<T> }, delay: number): Promise<T> {
181 this.task = task;
182 this.cancelTimeout();
183
184 if (!this.completionPromise) {
185 this.completionPromise = new Promise((resolve, reject) => {
186 this.doResolve = resolve;
187 this.doReject = reject;
188 }).then(() => {
189 this.completionPromise = null;
190 this.doResolve = null;
191 if (this.task) {
192 const task = this.task;
193 this.task = null;
194 return task();
195 }
196 return undefined;
197 });
198 }
199
200 this.timeout = setTimeout(() => {
201 this.timeout = null;
202 if (this.doResolve) {
203 this.doResolve(null);
204 }
205 }, delay);
206
207 return this.completionPromise;
208 }
209
210 public isRunning(): boolean {
211 return this.timeout !== null;
212 }
213
214 public cancel(): void {
215 this.cancelTimeout();
216
217 if (this.completionPromise) {
218 if (this.doReject) {
219 this.doReject(new Error("Canceled"));
220 }
221 this.completionPromise = null;
222 }
223 }
224
225 public dispose(): void {
226 this.cancel();
227 }
228
229 private cancelTimeout(): void {
230 if (this.timeout !== null) {
231 clearTimeout(this.timeout);
232 this.timeout = null;
233 }
234 }
235}
236