refactor: Deferred to a class (#10282)

This commit is contained in:
Nikolay Vitkov 2023-05-31 23:36:19 +02:00 committed by GitHub
parent 5fc136eec1
commit 39e9737232
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 164 additions and 149 deletions

View File

@ -194,6 +194,12 @@ module.exports = {
selector: "CallExpression[callee.name='require']",
message: '`require` statements are not allowed. Use `import`.',
},
{
// We need this as NodeJS will run until all the timers have resolved
message: 'Use method `Deferred.race()` instead.',
selector:
'MemberExpression[object.name="Promise"][property.name="race"]',
},
],
'@typescript-eslint/no-floating-promises': [
'error',

View File

@ -61,7 +61,6 @@ import {
import type {WebWorker} from '../common/WebWorker.js';
import {assert} from '../util/assert.js';
import {Deferred} from '../util/Deferred.js';
import {createDeferred} from '../util/Deferred.js';
import type {Browser} from './Browser.js';
import type {BrowserContext} from './BrowserContext.js';
@ -1638,8 +1637,8 @@ export class Page extends EventEmitter {
timeout: number,
closedDeferred: Deferred<TargetCloseError>
): Promise<void> {
const idleDeferred = createDeferred<void>();
const abortDeferred = createDeferred<Error>();
const idleDeferred = Deferred.create<void>();
const abortDeferred = Deferred.create<Error>();
let idleTimer: NodeJS.Timeout | undefined;
const cleanup = () => {
@ -1651,7 +1650,9 @@ export class Page extends EventEmitter {
clearTimeout(idleTimer);
if (networkManager.inFlightRequestsCount() === 0) {
idleTimer = setTimeout(idleDeferred.resolve, idleTime);
idleTimer = setTimeout(() => {
return idleDeferred.resolve();
}, idleTime);
}
};
@ -1676,11 +1677,7 @@ export class Page extends EventEmitter {
evaluate();
await Promise.race([
idleDeferred.valueOrThrow(),
...eventPromises,
closedDeferred.valueOrThrow(),
]).then(
await Deferred.race([idleDeferred, ...eventPromises, closedDeferred]).then(
r => {
cleanup();
return r;

View File

@ -33,7 +33,7 @@ import {
import {BrowserContext} from '../api/BrowserContext.js';
import {Page} from '../api/Page.js';
import {assert} from '../util/assert.js';
import {createDeferred} from '../util/Deferred.js';
import {Deferred} from '../util/Deferred.js';
import {ChromeTargetManager} from './ChromeTargetManager.js';
import {CDPSession, Connection, ConnectionEmittedEvents} from './Connection.js';
@ -501,7 +501,7 @@ export class CDPBrowser extends BrowserBase {
options: WaitForTargetOptions = {}
): Promise<Target> {
const {timeout = 30000} = options;
const targetDeferred = createDeferred<Target | PromiseLike<Target>>();
const targetDeferred = Deferred.create<Target | PromiseLike<Target>>();
this.on(BrowserEmittedEvents.TargetCreated, check);
this.on(BrowserEmittedEvents.TargetChanged, check);

View File

@ -18,7 +18,7 @@ import {Protocol} from 'devtools-protocol';
import {TargetFilterCallback} from '../api/Browser.js';
import {assert} from '../util/assert.js';
import {createDeferred} from '../util/Deferred.js';
import {Deferred} from '../util/Deferred.js';
import {CDPSession, Connection} from './Connection.js';
import {EventEmitter} from './EventEmitter.js';
@ -81,7 +81,7 @@ export class ChromeTargetManager extends EventEmitter implements TargetManager {
(event: Protocol.Target.DetachedFromTargetEvent) => void
> = new WeakMap();
#initializeDeferred = createDeferred<void>();
#initializeDeferred = Deferred.create<void>();
#targetsIdsForInit: Set<string> = new Set();
constructor(

View File

@ -18,7 +18,7 @@ import {Protocol} from 'devtools-protocol';
import {ProtocolMapping} from 'devtools-protocol/types/protocol-mapping.js';
import {assert} from '../util/assert.js';
import {createDeferred, Deferred} from '../util/util.js';
import {Deferred} from '../util/util.js';
import {ConnectionTransport} from './ConnectionTransport.js';
import {debug} from './Debug.js';
@ -64,7 +64,7 @@ function createIncrementalIdGenerator(): GetIdFn {
export class Callback {
#id: number;
#error = new ProtocolError();
#deferred = createDeferred<unknown>();
#deferred = Deferred.create<unknown>();
#timer?: ReturnType<typeof setTimeout>;
#label: string;

View File

@ -18,7 +18,7 @@ import Protocol from 'devtools-protocol';
import {WaitTimeoutOptions} from '../api/Page.js';
import {assert} from '../util/assert.js';
import {createDeferred, Deferred} from '../util/Deferred.js';
import {Deferred} from '../util/Deferred.js';
import {CDPSession} from './Connection.js';
import {TimeoutSettings} from './TimeoutSettings.js';
@ -151,7 +151,7 @@ export class DeviceRequestPrompt {
}
const {timeout = this.#timeoutSettings.timeout()} = options;
const deferred = createDeferred<DeviceRequestPromptDevice>({
const deferred = Deferred.create<DeviceRequestPromptDevice>({
message: `Waiting for \`DeviceRequestPromptDevice\` failed: ${timeout}ms exceeded`,
timeout,
});
@ -250,7 +250,7 @@ export class DeviceRequestPromptManager {
}
const {timeout = this.#timeoutSettings.timeout()} = options;
const deferred = createDeferred<DeviceRequestPrompt>({
const deferred = Deferred.create<DeviceRequestPrompt>({
message: `Waiting for \`DeviceRequestPrompt\` failed: ${timeout}ms exceeded`,
timeout,
});

View File

@ -18,7 +18,7 @@ import {Protocol} from 'devtools-protocol';
import {TargetFilterCallback} from '../api/Browser.js';
import {assert} from '../util/assert.js';
import {createDeferred} from '../util/Deferred.js';
import {Deferred} from '../util/Deferred.js';
import {CDPSession, Connection} from './Connection.js';
import {EventEmitter} from './EventEmitter.js';
@ -88,7 +88,7 @@ export class FirefoxTargetManager
(event: Protocol.Target.AttachedToTargetEvent) => Promise<void>
> = new WeakMap();
#initializeDeferred = createDeferred<void>();
#initializeDeferred = Deferred.create<void>();
#targetsIdsForInit: Set<string> = new Set();
constructor(

View File

@ -26,6 +26,7 @@ import {
import {HTTPResponse} from '../api/HTTPResponse.js';
import {Page, WaitTimeoutOptions} from '../api/Page.js';
import {assert} from '../util/assert.js';
import {Deferred} from '../util/Deferred.js';
import {isErrorLike} from '../util/ErrorLike.js';
import {CDPSession} from './Connection.js';
@ -123,7 +124,7 @@ export class Frame extends BaseFrame {
waitUntil,
timeout
);
let error = await Promise.race([
let error = await Deferred.race([
navigate(
this.#client,
url,
@ -134,7 +135,7 @@ export class Frame extends BaseFrame {
watcher.timeoutOrTerminationPromise(),
]);
if (!error) {
error = await Promise.race([
error = await Deferred.race([
watcher.timeoutOrTerminationPromise(),
ensureNewDocumentNavigation
? watcher.newDocumentNavigationPromise()
@ -197,7 +198,7 @@ export class Frame extends BaseFrame {
waitUntil,
timeout
);
const error = await Promise.race([
const error = await Deferred.race([
watcher.timeoutOrTerminationPromise(),
watcher.sameDocumentNavigationPromise(),
watcher.newDocumentNavigationPromise(),
@ -389,8 +390,8 @@ export class Frame extends BaseFrame {
return this.worlds[MAIN_WORLD].transferHandle(
await this.worlds[PUPPETEER_WORLD].evaluateHandle(
async ({createDeferred}, {url, id, type, content}) => {
const deferred = createDeferred<void>();
async ({Deferred}, {url, id, type, content}) => {
const deferred = Deferred.create<void>();
const script = document.createElement('script');
script.type = type;
script.text = content;
@ -457,8 +458,8 @@ export class Frame extends BaseFrame {
return this.worlds[MAIN_WORLD].transferHandle(
await this.worlds[PUPPETEER_WORLD].evaluateHandle(
async ({createDeferred}, {url, content}) => {
const deferred = createDeferred<void>();
async ({Deferred}, {url, content}) => {
const deferred = Deferred.create<void>();
let element: HTMLStyleElement | HTMLLinkElement;
if (!url) {
element = document.createElement('style');

View File

@ -15,7 +15,7 @@
*/
import {Frame as BaseFrame} from '../api/Frame.js';
import {createDeferred, Deferred} from '../util/Deferred.js';
import {Deferred} from '../util/Deferred.js';
/**
* Keeps track of the page frame tree and it's is managed by
@ -50,7 +50,7 @@ export class FrameTree<Frame extends BaseFrame> {
if (frame) {
return Promise.resolve(frame);
}
const deferred = createDeferred<Frame>();
const deferred = Deferred.create<Frame>();
const callbacks =
this.#waitRequests.get(frameId) || new Set<Deferred<Frame>>();
callbacks.add(deferred);

View File

@ -20,7 +20,7 @@ import {
HTTPResponse as BaseHTTPResponse,
RemoteAddress,
} from '../api/HTTPResponse.js';
import {createDeferred} from '../util/Deferred.js';
import {Deferred} from '../util/Deferred.js';
import {CDPSession} from './Connection.js';
import {ProtocolError} from './Errors.js';
@ -34,7 +34,7 @@ export class HTTPResponse extends BaseHTTPResponse {
#client: CDPSession;
#request: HTTPRequest;
#contentPromise: Promise<Buffer> | null = null;
#bodyLoadedDeferred = createDeferred<Error | void>();
#bodyLoadedDeferred = Deferred.create<Error | void>();
#remoteAddress: RemoteAddress;
#status: number;
#statusText: string;

View File

@ -19,7 +19,7 @@ import {Protocol} from 'devtools-protocol';
import type {ClickOptions, ElementHandle} from '../api/ElementHandle.js';
import {JSHandle} from '../api/JSHandle.js';
import {assert} from '../util/assert.js';
import {createDeferred} from '../util/Deferred.js';
import {Deferred} from '../util/Deferred.js';
import {Binding} from './Binding.js';
import {CDPSession} from './Connection.js';
@ -101,7 +101,7 @@ export interface IsolatedWorldChart {
export class IsolatedWorld {
#frame: Frame;
#document?: ElementHandle<Document>;
#context = createDeferred<ExecutionContext>();
#context = Deferred.create<ExecutionContext>();
#detached = false;
// Set of bindings that have been registered in the current context.
@ -144,7 +144,7 @@ export class IsolatedWorld {
clearContext(): void {
this.#document = undefined;
this.#context = createDeferred();
this.#context = Deferred.create();
}
setContext(context: ExecutionContext): void {
@ -304,7 +304,7 @@ export class IsolatedWorld {
waitUntil,
timeout
);
const error = await Promise.race([
const error = await Deferred.race<void | Error | undefined>([
watcher.timeoutOrTerminationPromise(),
watcher.lifecyclePromise(),
]);

View File

@ -16,7 +16,7 @@
import {HTTPResponse} from '../api/HTTPResponse.js';
import {assert} from '../util/assert.js';
import {Deferred, createDeferred} from '../util/Deferred.js';
import {Deferred} from '../util/Deferred.js';
import {CDPSessionEmittedEvents} from './Connection.js';
import {TimeoutError} from './Errors.js';
@ -71,10 +71,10 @@ export class LifecycleWatcher {
#eventListeners: PuppeteerEventListener[];
#initialLoaderId: string;
#sameDocumentNavigationDeferred = createDeferred<Error | undefined>();
#lifecycleDeferred = createDeferred<void>();
#newDocumentNavigationDeferred = createDeferred<Error | undefined>();
#terminationDeferred = createDeferred<Error | undefined>();
#sameDocumentNavigationDeferred = Deferred.create<undefined>();
#lifecycleDeferred = Deferred.create<void>();
#newDocumentNavigationDeferred = Deferred.create<undefined>();
#terminationDeferred = Deferred.create<Error>();
#timeoutPromise: Promise<TimeoutError | undefined>;
@ -169,7 +169,7 @@ export class LifecycleWatcher {
// navigation requests reported by the backend. This generally should not
// happen by it looks like it's possible.
this.#navigationResponseReceived?.resolve();
this.#navigationResponseReceived = createDeferred();
this.#navigationResponseReceived = Deferred.create();
if (request.response() !== null) {
this.#navigationResponseReceived?.resolve();
}
@ -222,10 +222,7 @@ export class LifecycleWatcher {
}
timeoutOrTerminationPromise(): Promise<Error | TimeoutError | undefined> {
return Promise.race([
this.#timeoutPromise,
this.#terminationDeferred.valueOrThrow(),
]);
return Deferred.race([this.#timeoutPromise, this.#terminationDeferred]);
}
async #createTimeoutPromise(): Promise<TimeoutError | undefined> {
@ -299,6 +296,6 @@ export class LifecycleWatcher {
dispose(): void {
removeEventListeners(this.#eventListeners);
this.#maximumTimer !== undefined && clearTimeout(this.#maximumTimer);
clearTimeout(this.#maximumTimer);
}
}

View File

@ -43,7 +43,7 @@ import {
NewDocumentScriptEvaluation,
} from '../api/Page.js';
import {assert} from '../util/assert.js';
import {createDeferred, Deferred} from '../util/Deferred.js';
import {Deferred} from '../util/Deferred.js';
import {isErrorLike} from '../util/ErrorLike.js';
import {Accessibility} from './Accessibility.js';
@ -153,7 +153,7 @@ export class CDPPage extends Page {
#screenshotTaskQueue: TaskQueue;
#workers = new Map<string, WebWorker>();
#fileChooserDeferreds = new Set<Deferred<FileChooser>>();
#sessionCloseDeferred = createDeferred<TargetCloseError>();
#sessionCloseDeferred = Deferred.create<TargetCloseError>();
#serviceWorkerBypassed = false;
#userDragInterceptionEnabled = false;
@ -374,7 +374,7 @@ export class CDPPage extends Page {
): Promise<FileChooser> {
const needsEnable = this.#fileChooserDeferreds.size === 0;
const {timeout = this.#timeoutSettings.timeout()} = options;
const deferred = createDeferred<FileChooser>({
const deferred = Deferred.create<FileChooser>({
message: `Waiting for \`FileChooser\` failed: ${timeout}ms exceeded`,
timeout,
});
@ -1029,7 +1029,7 @@ export class CDPPage extends Page {
};
}
const eventRace: Promise<Frame> = Promise.race([
const eventRace: Promise<Frame> = Deferred.race([
waitForEvent(
this.#frameManager,
FrameManagerEmittedEvents.FrameAttached,

View File

@ -19,7 +19,7 @@ import {Protocol} from 'devtools-protocol';
import type {Browser} from '../api/Browser.js';
import type {BrowserContext} from '../api/BrowserContext.js';
import {Page, PageEmittedEvents} from '../api/Page.js';
import {createDeferred} from '../util/Deferred.js';
import {Deferred} from '../util/Deferred.js';
import {CDPSession} from './Connection.js';
import {CDPPage} from './Page.js';
@ -55,11 +55,11 @@ export class Target {
/**
* @internal
*/
_initializedDeferred = createDeferred<InitializationStatus>();
_initializedDeferred = Deferred.create<InitializationStatus>();
/**
* @internal
*/
_isClosedDeferred = createDeferred<void>();
_isClosedDeferred = Deferred.create<void>();
/**
* @internal
*/

View File

@ -17,7 +17,7 @@
import {ElementHandle} from '../api/ElementHandle.js';
import {JSHandle} from '../api/JSHandle.js';
import type {Poller} from '../injected/Poller.js';
import {createDeferred} from '../util/Deferred.js';
import {Deferred} from '../util/Deferred.js';
import {isErrorLike} from '../util/ErrorLike.js';
import {stringifyFunction} from '../util/Function.js';
@ -49,7 +49,7 @@ export class WaitTask<T = unknown> {
#timeout?: NodeJS.Timeout;
#result = createDeferred<HandleFor<T>>();
#result = Deferred.create<HandleFor<T>>();
#poller?: JSHandle<Poller<T>>;
#signal?: AbortSignal;

View File

@ -15,7 +15,7 @@
*/
import {Protocol} from 'devtools-protocol';
import {createDeferred} from '../util/Deferred.js';
import {Deferred} from '../util/Deferred.js';
import {CDPSession} from './Connection.js';
import {ConsoleMessageType} from './ConsoleMessage.js';
@ -68,7 +68,7 @@ export type ExceptionThrownCallback = (
* @public
*/
export class WebWorker extends EventEmitter {
#executionContext = createDeferred<ExecutionContext>();
#executionContext = Deferred.create<ExecutionContext>();
#client: CDPSession;
#url: string;

View File

@ -25,7 +25,7 @@ import {
WaitForOptions,
} from '../../api/Page.js';
import {assert} from '../../util/assert.js';
import {createDeferred} from '../../util/Deferred.js';
import {Deferred} from '../../util/Deferred.js';
import {ConsoleMessage, ConsoleMessageLocation} from '../ConsoleMessage.js';
import {TargetCloseError} from '../Errors.js';
import {Handler} from '../EventEmitter.js';
@ -61,7 +61,7 @@ export class Page extends PageBase {
#frameTree = new FrameTree<Frame>();
#networkManager: NetworkManager;
#viewport: Viewport | null = null;
#closedDeferred = createDeferred<TargetCloseError>();
#closedDeferred = Deferred.create<TargetCloseError>();
#subscribedEvents = new Map<string, Handler<any>>([
['log.entryAdded', this.#onLogEntryAdded.bind(this)],
[

View File

@ -22,8 +22,8 @@ import type {ElementHandle} from '../api/ElementHandle.js';
import type {JSHandle} from '../api/JSHandle.js';
import {Page} from '../api/Page.js';
import {isNode} from '../environment.js';
import {Deferred} from '../puppeteer-core.js';
import {assert} from '../util/assert.js';
import {createDeferred} from '../util/Deferred.js';
import {isErrorLike} from '../util/ErrorLike.js';
import type {CDPSession} from './Connection.js';
@ -382,7 +382,7 @@ export async function waitForEvent<T>(
timeout: number,
abortPromise: Promise<Error>
): Promise<T> {
const deferred = createDeferred<T>({
const deferred = Deferred.create<T>({
message: `Timeout exceeded while waiting for event ${String(eventName)}`,
timeout,
});
@ -391,23 +391,19 @@ export async function waitForEvent<T>(
deferred.resolve(event);
}
});
return Promise.race([deferred.valueOrThrow(), abortPromise])
.then(
r => {
removeEventListeners([listener]);
if (isErrorLike(r)) {
throw r;
}
return r;
},
error => {
removeEventListeners([listener]);
throw error;
return Deferred.race<T | Error>([deferred, abortPromise]).then(
r => {
removeEventListeners([listener]);
if (isErrorLike(r)) {
throw r;
}
)
.finally(() => {
deferred.reject(new Error('Cleared'));
});
return r;
},
error => {
removeEventListeners([listener]);
throw error;
}
);
}
/**
@ -509,12 +505,12 @@ export async function waitWithTimeout<T>(
taskName: string,
timeout: number
): Promise<T> {
const deferred = createDeferred<never>({
const deferred = Deferred.create<never>({
message: `waiting for ${taskName} failed: timeout ${timeout}ms exceeded`,
timeout,
});
return await Promise.race([promise, deferred.valueOrThrow()]).finally(() => {
return await Deferred.race([promise, deferred.valueOrThrow()]).finally(() => {
deferred.reject(new Error('Cleared'));
});
}

View File

@ -15,7 +15,7 @@
*/
import {assert} from '../util/assert.js';
import {createDeferred, Deferred} from '../util/Deferred.js';
import {Deferred} from '../util/Deferred.js';
/**
* @internal
@ -42,7 +42,7 @@ export class MutationPoller<T> implements Poller<T> {
}
async start(): Promise<void> {
const deferred = (this.#deferred = createDeferred<T>());
const deferred = (this.#deferred = Deferred.create<T>());
const result = await this.#fn();
if (result) {
deferred.resolve(result);
@ -92,7 +92,7 @@ export class RAFPoller<T> implements Poller<T> {
}
async start(): Promise<void> {
const deferred = (this.#deferred = createDeferred<T>());
const deferred = (this.#deferred = Deferred.create<T>());
const result = await this.#fn();
if (result) {
deferred.resolve(result);
@ -143,7 +143,7 @@ export class IntervalPoller<T> implements Poller<T> {
}
async start(): Promise<void> {
const deferred = (this.#deferred = createDeferred<T>());
const deferred = (this.#deferred = Deferred.create<T>());
const result = await this.#fn();
if (result) {
deferred.resolve(result);

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
import {createDeferred} from '../util/Deferred.js';
import {Deferred} from '../util/Deferred.js';
import {createFunction} from '../util/Function.js';
import * as ARIAQuerySelector from './ARIAQuerySelector.js';
@ -41,7 +41,7 @@ const PuppeteerUtil = Object.freeze({
...TextQuerySelector,
...util,
...XPathQuerySelector,
createDeferred,
Deferred,
createFunction,
createTextContent,
IntervalPoller,

View File

@ -1,6 +1,6 @@
import {DEFERRED_PROMISE_DEBUG_TIMEOUT} from '../environment.js';
import {Deferred, createDeferred} from './Deferred.js';
import {Deferred} from './Deferred.js';
/**
* Creates and returns a deferred promise using DEFERRED_PROMISE_DEBUG_TIMEOUT
@ -10,10 +10,10 @@ import {Deferred, createDeferred} from './Deferred.js';
*/
export function createDebuggableDeferred<T>(message: string): Deferred<T> {
if (DEFERRED_PROMISE_DEBUG_TIMEOUT > 0) {
return createDeferred({
return Deferred.create({
message,
timeout: DEFERRED_PROMISE_DEBUG_TIMEOUT,
});
}
return createDeferred();
return Deferred.create();
}

View File

@ -1,17 +1,5 @@
import {TimeoutError} from '../common/Errors.js';
/**
* @internal
*/
export interface Deferred<T> {
finished: () => boolean;
resolved: () => boolean;
resolve: (value: T) => void;
reject: (error: Error) => void;
value: () => T | Error | undefined;
valueOrThrow: () => Promise<T>;
}
/**
* @internal
*/
@ -29,61 +17,94 @@ export interface DeferredOptions {
*
* @internal
*/
export function createDeferred<T>(opts?: DeferredOptions): Deferred<T> {
let isResolved = false;
let isRejected = false;
let _value: T | Error | undefined;
let resolver: (value: void) => void;
const taskPromise = new Promise<void>(resolve => {
resolver = resolve;
export class Deferred<T> {
#isResolved = false;
#isRejected = false;
#value: T | Error | undefined;
#resolver: (value: void) => void = () => {};
#taskPromise = new Promise<void>(resolve => {
this.#resolver = resolve;
});
const timeoutId =
opts && opts.timeout > 0
? setTimeout(() => {
reject(new TimeoutError(opts.message));
}, opts.timeout)
: undefined;
#timeoutId: ReturnType<typeof setTimeout> | undefined;
function finish(value: T | Error) {
clearTimeout(timeoutId);
_value = value;
resolver();
constructor(opts?: DeferredOptions) {
this.#timeoutId =
opts && opts.timeout > 0
? setTimeout(() => {
this.reject(new TimeoutError(opts.message));
}, opts.timeout)
: undefined;
}
function resolve(value: T) {
if (isRejected || isResolved) {
#finish(value: T | Error) {
clearTimeout(this.#timeoutId);
this.#value = value;
this.#resolver();
}
resolve(value: T): void {
if (this.#isRejected || this.#isResolved) {
return;
}
isResolved = true;
finish(value);
this.#isResolved = true;
this.#finish(value);
}
function reject(error: Error) {
if (isRejected || isResolved) {
reject(error: Error): void {
if (this.#isRejected || this.#isResolved) {
return;
}
isRejected = true;
finish(error);
this.#isRejected = true;
this.#finish(error);
}
return {
resolved: () => {
return isResolved;
},
finished: () => {
return isResolved || isRejected;
},
resolve,
reject,
value: () => {
return _value;
},
async valueOrThrow() {
await taskPromise;
if (isRejected) {
throw _value;
resolved(): boolean {
return this.#isResolved;
}
finished(): boolean {
return this.#isResolved || this.#isRejected;
}
value(): T | Error | undefined {
return this.#value;
}
async valueOrThrow(): Promise<T> {
await this.#taskPromise;
if (this.#isRejected) {
throw this.#value;
}
return this.#value as T;
}
static create<R>(opts?: DeferredOptions): Deferred<R> {
return new Deferred<R>(opts);
}
static async race<R>(
awaitables: Array<Promise<R> | Deferred<R>>
): Promise<R> {
const deferredWithTimeout: Set<Deferred<R>> = new Set();
try {
const promises = awaitables.map(value => {
if (value instanceof Deferred) {
deferredWithTimeout.add(value);
return value.valueOrThrow();
}
return value;
});
// eslint-disable-next-line no-restricted-syntax
return await Promise.race(promises);
} finally {
for (const deferred of deferredWithTimeout) {
// We need to stop the timeout else
// Node.JS will keep running the event loop till the
// timer executes
deferred.reject(new Error('Timeout cleared'));
}
return _value as T;
},
};
}
}
}

View File

@ -15,10 +15,7 @@
*/
import expect from 'expect';
import {
Deferred,
createDeferred,
} from 'puppeteer-core/internal/util/Deferred.js';
import {Deferred} from 'puppeteer-core/internal/util/Deferred.js';
describe('DeferredPromise', function () {
it('should catch errors', async () => {
@ -30,7 +27,7 @@ describe('DeferredPromise', function () {
}
// Async function that fails.
function fails(): Deferred<void> {
const deferred = createDeferred<void>();
const deferred = Deferred.create<void>();
setTimeout(() => {
deferred.reject(new Error('test'));
}, 25);