mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
chore: refactor deferred promise (#8759)
This commit is contained in:
parent
50bcff2ec2
commit
8be8f5ff72
@ -28,7 +28,9 @@ import {getQueryHandlerAndSelector} from './QueryHandler.js';
|
|||||||
import {TimeoutSettings} from './TimeoutSettings.js';
|
import {TimeoutSettings} from './TimeoutSettings.js';
|
||||||
import {EvaluateFunc, HandleFor, NodeFor} from './types.js';
|
import {EvaluateFunc, HandleFor, NodeFor} from './types.js';
|
||||||
import {
|
import {
|
||||||
|
createDeferredPromise,
|
||||||
debugError,
|
debugError,
|
||||||
|
DeferredPromise,
|
||||||
importFS,
|
importFS,
|
||||||
isNumber,
|
isNumber,
|
||||||
isString,
|
isString,
|
||||||
@ -75,8 +77,7 @@ export class DOMWorld {
|
|||||||
#frame: Frame;
|
#frame: Frame;
|
||||||
#timeoutSettings: TimeoutSettings;
|
#timeoutSettings: TimeoutSettings;
|
||||||
#documentPromise: Promise<ElementHandle<Document>> | null = null;
|
#documentPromise: Promise<ElementHandle<Document>> | null = null;
|
||||||
#contextPromise: Promise<ExecutionContext> | null = null;
|
#contextPromise: DeferredPromise<ExecutionContext> = createDeferredPromise();
|
||||||
#contextResolveCallback: ((x: ExecutionContext) => void) | null = null;
|
|
||||||
#detached = false;
|
#detached = false;
|
||||||
|
|
||||||
// Set of bindings that have been registered in the current context.
|
// Set of bindings that have been registered in the current context.
|
||||||
@ -110,7 +111,6 @@ export class DOMWorld {
|
|||||||
this.#frameManager = frameManager;
|
this.#frameManager = frameManager;
|
||||||
this.#frame = frame;
|
this.#frame = frame;
|
||||||
this.#timeoutSettings = timeoutSettings;
|
this.#timeoutSettings = timeoutSettings;
|
||||||
this._setContext(null);
|
|
||||||
this.#client.on('Runtime.bindingCalled', this.#onBindingCalled);
|
this.#client.on('Runtime.bindingCalled', this.#onBindingCalled);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,28 +118,25 @@ export class DOMWorld {
|
|||||||
return this.#frame;
|
return this.#frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _setContext(context: ExecutionContext | null): Promise<void> {
|
clearContext(): void {
|
||||||
if (context) {
|
this.#documentPromise = null;
|
||||||
|
this.#contextPromise = createDeferredPromise();
|
||||||
|
}
|
||||||
|
|
||||||
|
setContext(context: ExecutionContext): void {
|
||||||
assert(
|
assert(
|
||||||
this.#contextResolveCallback,
|
this.#contextPromise,
|
||||||
`ExecutionContext ${context._contextId} has already been set.`
|
`ExecutionContext ${context._contextId} has already been set.`
|
||||||
);
|
);
|
||||||
this.#ctxBindings.clear();
|
this.#ctxBindings.clear();
|
||||||
this.#contextResolveCallback?.call(null, context);
|
this.#contextPromise.resolve(context);
|
||||||
this.#contextResolveCallback = null;
|
|
||||||
for (const waitTask of this._waitTasks) {
|
for (const waitTask of this._waitTasks) {
|
||||||
waitTask.rerun();
|
waitTask.rerun();
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
this.#documentPromise = null;
|
|
||||||
this.#contextPromise = new Promise(fulfill => {
|
|
||||||
this.#contextResolveCallback = fulfill;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_hasContext(): boolean {
|
hasContext(): boolean {
|
||||||
return !this.#contextResolveCallback;
|
return this.#contextPromise.resolved();
|
||||||
}
|
}
|
||||||
|
|
||||||
_detach(): void {
|
_detach(): void {
|
||||||
@ -619,7 +616,7 @@ export class DOMWorld {
|
|||||||
event: Protocol.Runtime.BindingCalledEvent
|
event: Protocol.Runtime.BindingCalledEvent
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
let payload: {type: string; name: string; seq: number; args: unknown[]};
|
let payload: {type: string; name: string; seq: number; args: unknown[]};
|
||||||
if (!this._hasContext()) {
|
if (!this.hasContext()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const context = await this.executionContext();
|
const context = await this.executionContext();
|
||||||
|
@ -397,7 +397,8 @@ export class FrameManager extends EventEmitter {
|
|||||||
return complete(parentFrame);
|
return complete(parentFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.#framesPendingTargetInit.has(parentFrameId)) {
|
const frame = this.#framesPendingTargetInit.get(parentFrameId);
|
||||||
|
if (frame) {
|
||||||
if (!this.#framesPendingAttachment.has(frameId)) {
|
if (!this.#framesPendingAttachment.has(frameId)) {
|
||||||
this.#framesPendingAttachment.set(
|
this.#framesPendingAttachment.set(
|
||||||
frameId,
|
frameId,
|
||||||
@ -406,7 +407,7 @@ export class FrameManager extends EventEmitter {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
this.#framesPendingTargetInit.get(parentFrameId)!.promise.then(() => {
|
frame.then(() => {
|
||||||
complete(this.#frames.get(parentFrameId)!);
|
complete(this.#frames.get(parentFrameId)!);
|
||||||
this.#framesPendingAttachment.get(frameId)?.resolve();
|
this.#framesPendingAttachment.get(frameId)?.resolve();
|
||||||
this.#framesPendingAttachment.delete(frameId);
|
this.#framesPendingAttachment.delete(frameId);
|
||||||
@ -455,8 +456,9 @@ export class FrameManager extends EventEmitter {
|
|||||||
|
|
||||||
this.emit(FrameManagerEmittedEvents.FrameNavigated, frame);
|
this.emit(FrameManagerEmittedEvents.FrameNavigated, frame);
|
||||||
};
|
};
|
||||||
if (this.#framesPendingAttachment.has(frameId)) {
|
const pendingFrame = this.#framesPendingAttachment.get(frameId);
|
||||||
this.#framesPendingAttachment.get(frameId)!.promise.then(() => {
|
if (pendingFrame) {
|
||||||
|
pendingFrame.then(() => {
|
||||||
complete(isMainFrame ? this.#mainFrame : this.#frames.get(frameId));
|
complete(isMainFrame ? this.#mainFrame : this.#frames.get(frameId));
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -539,7 +541,7 @@ export class FrameManager extends EventEmitter {
|
|||||||
world = frame._mainWorld;
|
world = frame._mainWorld;
|
||||||
} else if (
|
} else if (
|
||||||
contextPayload.name === UTILITY_WORLD_NAME &&
|
contextPayload.name === UTILITY_WORLD_NAME &&
|
||||||
!frame._secondaryWorld._hasContext()
|
!frame._secondaryWorld.hasContext()
|
||||||
) {
|
) {
|
||||||
// In case of multiple sessions to the same target, there's a race between
|
// In case of multiple sessions to the same target, there's a race between
|
||||||
// connections so we might end up creating multiple isolated worlds.
|
// connections so we might end up creating multiple isolated worlds.
|
||||||
@ -553,7 +555,7 @@ export class FrameManager extends EventEmitter {
|
|||||||
world
|
world
|
||||||
);
|
);
|
||||||
if (world) {
|
if (world) {
|
||||||
world._setContext(context);
|
world.setContext(context);
|
||||||
}
|
}
|
||||||
const key = `${session.id()}:${contextPayload.id}`;
|
const key = `${session.id()}:${contextPayload.id}`;
|
||||||
this.#contextIdToContext.set(key, context);
|
this.#contextIdToContext.set(key, context);
|
||||||
@ -570,7 +572,7 @@ export class FrameManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
this.#contextIdToContext.delete(key);
|
this.#contextIdToContext.delete(key);
|
||||||
if (context._world) {
|
if (context._world) {
|
||||||
context._world._setContext(null);
|
context._world.clearContext();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -582,7 +584,7 @@ export class FrameManager extends EventEmitter {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (context._world) {
|
if (context._world) {
|
||||||
context._world._setContext(null);
|
context._world.clearContext();
|
||||||
}
|
}
|
||||||
this.#contextIdToContext.delete(key);
|
this.#contextIdToContext.delete(key);
|
||||||
}
|
}
|
||||||
|
@ -143,7 +143,7 @@ export class NetworkManager extends EventEmitter {
|
|||||||
*/
|
*/
|
||||||
initialize(): Promise<void> {
|
initialize(): Promise<void> {
|
||||||
if (this.#deferredInitPromise) {
|
if (this.#deferredInitPromise) {
|
||||||
return this.#deferredInitPromise.promise;
|
return this.#deferredInitPromise;
|
||||||
}
|
}
|
||||||
this.#deferredInitPromise = createDeferredPromiseWithTimer<void>(
|
this.#deferredInitPromise = createDeferredPromiseWithTimer<void>(
|
||||||
'NetworkManager initialization timed out',
|
'NetworkManager initialization timed out',
|
||||||
@ -157,14 +157,15 @@ export class NetworkManager extends EventEmitter {
|
|||||||
: null,
|
: null,
|
||||||
this.#client.send('Network.enable'),
|
this.#client.send('Network.enable'),
|
||||||
]);
|
]);
|
||||||
|
const deferredInitPromise = this.#deferredInitPromise;
|
||||||
init
|
init
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.#deferredInitPromise?.resolve();
|
deferredInitPromise.resolve();
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
return this.#deferredInitPromise?.reject(err);
|
deferredInitPromise.reject(err);
|
||||||
});
|
});
|
||||||
return this.#deferredInitPromise.promise;
|
return this.#deferredInitPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
async authenticate(credentials?: Credentials): Promise<void> {
|
async authenticate(credentials?: Credentials): Promise<void> {
|
||||||
|
@ -527,11 +527,11 @@ export function isErrnoException(obj: unknown): obj is NodeJS.ErrnoException {
|
|||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export type DeferredPromise<T> = {
|
export interface DeferredPromise<T> extends Promise<T> {
|
||||||
promise: Promise<T>;
|
resolved: () => boolean;
|
||||||
resolve: (_: T) => void;
|
resolve: (_: T) => void;
|
||||||
reject: (_: Error) => void;
|
reject: (_: Error) => void;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an returns a promise along with the resolve/reject functions.
|
* Creates an returns a promise along with the resolve/reject functions.
|
||||||
@ -545,6 +545,7 @@ export function createDeferredPromiseWithTimer<T>(
|
|||||||
timeoutMessage: string,
|
timeoutMessage: string,
|
||||||
timeout = 5000
|
timeout = 5000
|
||||||
): DeferredPromise<T> {
|
): DeferredPromise<T> {
|
||||||
|
let isResolved = false;
|
||||||
let resolver = (_: T): void => {};
|
let resolver = (_: T): void => {};
|
||||||
let rejector = (_: Error) => {};
|
let rejector = (_: Error) => {};
|
||||||
const taskPromise = new Promise<T>((resolve, reject) => {
|
const taskPromise = new Promise<T>((resolve, reject) => {
|
||||||
@ -554,15 +555,45 @@ export function createDeferredPromiseWithTimer<T>(
|
|||||||
const timeoutId = setTimeout(() => {
|
const timeoutId = setTimeout(() => {
|
||||||
rejector(new Error(timeoutMessage));
|
rejector(new Error(timeoutMessage));
|
||||||
}, timeout);
|
}, timeout);
|
||||||
return {
|
return Object.assign(taskPromise, {
|
||||||
promise: taskPromise,
|
resolved: () => {
|
||||||
|
return isResolved;
|
||||||
|
},
|
||||||
resolve: (value: T) => {
|
resolve: (value: T) => {
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
|
isResolved = true;
|
||||||
resolver(value);
|
resolver(value);
|
||||||
},
|
},
|
||||||
reject: (err: Error) => {
|
reject: (err: Error) => {
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
rejector(err);
|
rejector(err);
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an returns a promise along with the resolve/reject functions.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export function createDeferredPromise<T>(): DeferredPromise<T> {
|
||||||
|
let isResolved = false;
|
||||||
|
let resolver = (_: T): void => {};
|
||||||
|
let rejector = (_: Error) => {};
|
||||||
|
const taskPromise = new Promise<T>((resolve, reject) => {
|
||||||
|
resolver = resolve;
|
||||||
|
rejector = reject;
|
||||||
|
});
|
||||||
|
return Object.assign(taskPromise, {
|
||||||
|
resolved: () => {
|
||||||
|
return isResolved;
|
||||||
|
},
|
||||||
|
resolve: (value: T) => {
|
||||||
|
isResolved = true;
|
||||||
|
resolver(value);
|
||||||
|
},
|
||||||
|
reject: (err: Error) => {
|
||||||
|
rejector(err);
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user