chore: move async utils into a separate file (#8595)
This commit is contained in:
parent
246ac6aea6
commit
1c169289b2
|
|
@ -23,9 +23,10 @@ import { Connection } from './connection';
|
||||||
import { Events } from './events';
|
import { Events } from './events';
|
||||||
import { ChildProcess } from 'child_process';
|
import { ChildProcess } from 'child_process';
|
||||||
import { envObjectToArray } from './clientHelper';
|
import { envObjectToArray } from './clientHelper';
|
||||||
import { assert, headersObjectToArray, getUserAgent, ManualPromise } from '../utils/utils';
|
import { assert, headersObjectToArray, getUserAgent } from '../utils/utils';
|
||||||
import * as api from '../../types/types';
|
import * as api from '../../types/types';
|
||||||
import { kBrowserClosedError } from '../utils/errors';
|
import { kBrowserClosedError } from '../utils/errors';
|
||||||
|
import { ManualPromise } from '../utils/async';
|
||||||
|
|
||||||
export interface BrowserServerLauncher {
|
export interface BrowserServerLauncher {
|
||||||
launchServer(options?: LaunchServerOptions): Promise<api.BrowserServer>;
|
launchServer(options?: LaunchServerOptions): Promise<api.BrowserServer>;
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,8 @@ import { Frame } from './frame';
|
||||||
import { Headers, RemoteAddr, SecurityDetails, WaitForEventOptions } from './types';
|
import { Headers, RemoteAddr, SecurityDetails, WaitForEventOptions } from './types';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import * as mime from 'mime';
|
import * as mime from 'mime';
|
||||||
import { isString, headersObjectToArray, headersArrayToObject, ManualPromise } from '../utils/utils';
|
import { isString, headersObjectToArray, headersArrayToObject } from '../utils/utils';
|
||||||
|
import { ManualPromise } from '../utils/async';
|
||||||
import { Events } from './events';
|
import { Events } from './events';
|
||||||
import { Page } from './page';
|
import { Page } from './page';
|
||||||
import { Waiter } from './waiter';
|
import { Waiter } from './waiter';
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { assert, ManualPromise } from '../utils/utils';
|
import { assert } from '../utils/utils';
|
||||||
|
import { ManualPromise } from '../utils/async';
|
||||||
import { SdkObject } from './instrumentation';
|
import { SdkObject } from './instrumentation';
|
||||||
|
|
||||||
type SaveCallback = (localPath: string, error?: string) => Promise<void>;
|
type SaveCallback = (localPath: string, error?: string) => Promise<void>;
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import * as dialog from '../dialog';
|
||||||
import * as dom from '../dom';
|
import * as dom from '../dom';
|
||||||
import * as frames from '../frames';
|
import * as frames from '../frames';
|
||||||
import { eventsHelper, RegisteredListener } from '../../utils/eventsHelper';
|
import { eventsHelper, RegisteredListener } from '../../utils/eventsHelper';
|
||||||
import { assert, ManualPromise } from '../../utils/utils';
|
import { assert } from '../../utils/utils';
|
||||||
import { Page, PageBinding, PageDelegate, Worker } from '../page';
|
import { Page, PageBinding, PageDelegate, Worker } from '../page';
|
||||||
import * as types from '../types';
|
import * as types from '../types';
|
||||||
import { getAccessibilityTree } from './ffAccessibility';
|
import { getAccessibilityTree } from './ffAccessibility';
|
||||||
|
|
@ -32,6 +32,7 @@ import { Protocol } from './protocol';
|
||||||
import { Progress } from '../progress';
|
import { Progress } from '../progress';
|
||||||
import { splitErrorMessage } from '../../utils/stackTrace';
|
import { splitErrorMessage } from '../../utils/stackTrace';
|
||||||
import { debugLogger } from '../../utils/debugLogger';
|
import { debugLogger } from '../../utils/debugLogger';
|
||||||
|
import { ManualPromise } from '../../utils/async';
|
||||||
|
|
||||||
export const UTILITY_WORLD_NAME = '__playwright_utility_world__';
|
export const UTILITY_WORLD_NAME = '__playwright_utility_world__';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,8 @@ import { Page } from './page';
|
||||||
import * as types from './types';
|
import * as types from './types';
|
||||||
import { BrowserContext } from './browserContext';
|
import { BrowserContext } from './browserContext';
|
||||||
import { Progress, ProgressController } from './progress';
|
import { Progress, ProgressController } from './progress';
|
||||||
import { assert, constructURLBasedOnBaseURL, makeWaitForNextTask, ManualPromise } from '../utils/utils';
|
import { assert, constructURLBasedOnBaseURL, makeWaitForNextTask } from '../utils/utils';
|
||||||
|
import { ManualPromise } from '../utils/async';
|
||||||
import { debugLogger } from '../utils/debugLogger';
|
import { debugLogger } from '../utils/debugLogger';
|
||||||
import { CallMetadata, internalCallMetadata, SdkObject } from './instrumentation';
|
import { CallMetadata, internalCallMetadata, SdkObject } from './instrumentation';
|
||||||
import { ElementStateWithoutStable } from './injected/injectedScript';
|
import { ElementStateWithoutStable } from './injected/injectedScript';
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,8 @@
|
||||||
|
|
||||||
import * as frames from './frames';
|
import * as frames from './frames';
|
||||||
import * as types from './types';
|
import * as types from './types';
|
||||||
import { assert, ManualPromise } from '../utils/utils';
|
import { assert } from '../utils/utils';
|
||||||
|
import { ManualPromise } from '../utils/async';
|
||||||
import { SdkObject } from './instrumentation';
|
import { SdkObject } from './instrumentation';
|
||||||
|
|
||||||
export function filterCookies(cookies: types.NetworkCookie[], urls: string[]): types.NetworkCookie[] {
|
export function filterCookies(cookies: types.NetworkCookie[], urls: string[]): types.NetworkCookie[] {
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,8 @@ import { ConsoleMessage } from './console';
|
||||||
import * as accessibility from './accessibility';
|
import * as accessibility from './accessibility';
|
||||||
import { FileChooser } from './fileChooser';
|
import { FileChooser } from './fileChooser';
|
||||||
import { Progress, ProgressController } from './progress';
|
import { Progress, ProgressController } from './progress';
|
||||||
import { assert, isError, ManualPromise } from '../utils/utils';
|
import { assert, isError } from '../utils/utils';
|
||||||
|
import { ManualPromise } from '../utils/async';
|
||||||
import { debugLogger } from '../utils/debugLogger';
|
import { debugLogger } from '../utils/debugLogger';
|
||||||
import { SelectorInfo, Selectors } from './selectors';
|
import { SelectorInfo, Selectors } from './selectors';
|
||||||
import { CallMetadata, SdkObject } from './instrumentation';
|
import { CallMetadata, SdkObject } from './instrumentation';
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,10 @@ import * as network from '../network';
|
||||||
import * as types from '../types';
|
import * as types from '../types';
|
||||||
import { Protocol } from './protocol';
|
import { Protocol } from './protocol';
|
||||||
import { WKSession } from './wkConnection';
|
import { WKSession } from './wkConnection';
|
||||||
import { assert, headersObjectToArray, headersArrayToObject, ManualPromise } from '../../utils/utils';
|
import { assert, headersObjectToArray, headersArrayToObject } from '../../utils/utils';
|
||||||
import { InterceptedResponse } from '../network';
|
import { InterceptedResponse } from '../network';
|
||||||
import { WKPage } from './wkPage';
|
import { WKPage } from './wkPage';
|
||||||
|
import { ManualPromise } from '../../utils/async';
|
||||||
|
|
||||||
const errorReasons: { [reason: string]: Protocol.Network.ResourceErrorType } = {
|
const errorReasons: { [reason: string]: Protocol.Network.ResourceErrorType } = {
|
||||||
'aborted': 'Cancellation',
|
'aborted': 'Cancellation',
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import * as jpeg from 'jpeg-js';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import * as png from 'pngjs';
|
import * as png from 'pngjs';
|
||||||
import { splitErrorMessage } from '../../utils/stackTrace';
|
import { splitErrorMessage } from '../../utils/stackTrace';
|
||||||
import { assert, createGuid, debugAssert, headersArrayToObject, headersObjectToArray, hostPlatform, ManualPromise } from '../../utils/utils';
|
import { assert, createGuid, debugAssert, headersArrayToObject, headersObjectToArray, hostPlatform } from '../../utils/utils';
|
||||||
import * as accessibility from '../accessibility';
|
import * as accessibility from '../accessibility';
|
||||||
import * as dialog from '../dialog';
|
import * as dialog from '../dialog';
|
||||||
import * as dom from '../dom';
|
import * as dom from '../dom';
|
||||||
|
|
@ -41,6 +41,7 @@ import { WKInterceptableRequest, WKRouteImpl } from './wkInterceptableRequest';
|
||||||
import { WKProvisionalPage } from './wkProvisionalPage';
|
import { WKProvisionalPage } from './wkProvisionalPage';
|
||||||
import { WKWorkers } from './wkWorkers';
|
import { WKWorkers } from './wkWorkers';
|
||||||
import { debugLogger } from '../../utils/debugLogger';
|
import { debugLogger } from '../../utils/debugLogger';
|
||||||
|
import { ManualPromise } from '../../utils/async';
|
||||||
|
|
||||||
const UTILITY_WORLD_NAME = '__playwright_utility_world__';
|
const UTILITY_WORLD_NAME = '__playwright_utility_world__';
|
||||||
const BINDING_CALL_MESSAGE = '__playwright_binding_call__';
|
const BINDING_CALL_MESSAGE = '__playwright_binding_call__';
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { promisify } from 'util';
|
import { promisify } from 'util';
|
||||||
import { Dispatcher, TestGroup } from './dispatcher';
|
import { Dispatcher, TestGroup } from './dispatcher';
|
||||||
import { createMatcher, FilePatternFilter, monotonicTime, raceAgainstDeadline } from './util';
|
import { createMatcher, FilePatternFilter, monotonicTime } from './util';
|
||||||
import { TestCase, Suite } from './test';
|
import { TestCase, Suite } from './test';
|
||||||
import { Loader } from './loader';
|
import { Loader } from './loader';
|
||||||
import { Reporter } from '../../types/testReporter';
|
import { Reporter } from '../../types/testReporter';
|
||||||
|
|
@ -36,6 +36,7 @@ import { ProjectImpl } from './project';
|
||||||
import { Minimatch } from 'minimatch';
|
import { Minimatch } from 'minimatch';
|
||||||
import { Config, FullConfig } from './types';
|
import { Config, FullConfig } from './types';
|
||||||
import { WebServer } from './webServer';
|
import { WebServer } from './webServer';
|
||||||
|
import { raceAgainstDeadline } from '../utils/async';
|
||||||
|
|
||||||
const removeFolderAsync = promisify(rimraf);
|
const removeFolderAsync = promisify(rimraf);
|
||||||
const readDirAsync = promisify(fs.readdir);
|
const readDirAsync = promisify(fs.readdir);
|
||||||
|
|
@ -95,7 +96,7 @@ export class Runner {
|
||||||
async run(list: boolean, filePatternFilters: FilePatternFilter[], projectName?: string): Promise<RunResultStatus> {
|
async run(list: boolean, filePatternFilters: FilePatternFilter[], projectName?: string): Promise<RunResultStatus> {
|
||||||
this._reporter = await this._createReporter(list);
|
this._reporter = await this._createReporter(list);
|
||||||
const config = this._loader.fullConfig();
|
const config = this._loader.fullConfig();
|
||||||
const globalDeadline = config.globalTimeout ? config.globalTimeout + monotonicTime() : undefined;
|
const globalDeadline = config.globalTimeout ? config.globalTimeout + monotonicTime() : 0;
|
||||||
const { result, timedOut } = await raceAgainstDeadline(this._run(list, filePatternFilters, projectName), globalDeadline);
|
const { result, timedOut } = await raceAgainstDeadline(this._run(list, filePatternFilters, projectName), globalDeadline);
|
||||||
if (timedOut) {
|
if (timedOut) {
|
||||||
if (!this._didBegin)
|
if (!this._didBegin)
|
||||||
|
|
|
||||||
|
|
@ -21,56 +21,6 @@ import type { TestError, Location } from './types';
|
||||||
import { default as minimatch } from 'minimatch';
|
import { default as minimatch } from 'minimatch';
|
||||||
import { errors } from '../..';
|
import { errors } from '../..';
|
||||||
|
|
||||||
export class DeadlineRunner<T> {
|
|
||||||
private _timer: NodeJS.Timer | undefined;
|
|
||||||
private _done = false;
|
|
||||||
private _fulfill!: (t: { result?: T, timedOut?: boolean }) => void;
|
|
||||||
private _reject!: (error: any) => void;
|
|
||||||
|
|
||||||
readonly result: Promise<{ result?: T, timedOut?: boolean }>;
|
|
||||||
|
|
||||||
constructor(promise: Promise<T>, deadline: number | undefined) {
|
|
||||||
this.result = new Promise((f, r) => {
|
|
||||||
this._fulfill = f;
|
|
||||||
this._reject = r;
|
|
||||||
});
|
|
||||||
promise.then(result => {
|
|
||||||
this._finish({ result });
|
|
||||||
}).catch(e => {
|
|
||||||
this._finish(undefined, e);
|
|
||||||
});
|
|
||||||
this.setDeadline(deadline);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _finish(success?: { result?: T, timedOut?: boolean }, error?: any) {
|
|
||||||
if (this._done)
|
|
||||||
return;
|
|
||||||
this.setDeadline(undefined);
|
|
||||||
if (success)
|
|
||||||
this._fulfill(success);
|
|
||||||
else
|
|
||||||
this._reject(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
setDeadline(deadline: number | undefined) {
|
|
||||||
if (this._timer) {
|
|
||||||
clearTimeout(this._timer);
|
|
||||||
this._timer = undefined;
|
|
||||||
}
|
|
||||||
if (deadline === undefined)
|
|
||||||
return;
|
|
||||||
const timeout = deadline - monotonicTime();
|
|
||||||
if (timeout <= 0)
|
|
||||||
this._finish({ timedOut: true });
|
|
||||||
else
|
|
||||||
this._timer = setTimeout(() => this._finish({ timedOut: true }), timeout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function raceAgainstDeadline<T>(promise: Promise<T>, deadline: number | undefined): Promise<{ result?: T, timedOut?: boolean }> {
|
|
||||||
return (new DeadlineRunner(promise, deadline)).result;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function pollUntilDeadline(testInfo: TestInfoImpl, func: (remainingTime: number) => Promise<boolean>, pollTime: number | undefined, deadlinePromise: Promise<void>): Promise<void> {
|
export async function pollUntilDeadline(testInfo: TestInfoImpl, func: (remainingTime: number) => Promise<boolean>, pollTime: number | undefined, deadlinePromise: Promise<void>): Promise<void> {
|
||||||
let defaultExpectTimeout = testInfo.project.expect?.timeout;
|
let defaultExpectTimeout = testInfo.project.expect?.timeout;
|
||||||
if (typeof defaultExpectTimeout === 'undefined')
|
if (typeof defaultExpectTimeout === 'undefined')
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,8 @@
|
||||||
import net from 'net';
|
import net from 'net';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import stream from 'stream';
|
import stream from 'stream';
|
||||||
import { monotonicTime, raceAgainstDeadline } from './util';
|
import { monotonicTime } from './util';
|
||||||
|
import { raceAgainstDeadline } from '../utils/async';
|
||||||
import { WebServerConfig } from '../../types/test';
|
import { WebServerConfig } from '../../types/test';
|
||||||
import { launchProcess } from '../utils/processLauncher';
|
import { launchProcess } from '../utils/processLauncher';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import rimraf from 'rimraf';
|
||||||
import util from 'util';
|
import util from 'util';
|
||||||
import colors from 'colors/safe';
|
import colors from 'colors/safe';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import { monotonicTime, DeadlineRunner, raceAgainstDeadline, serializeError, sanitizeForFilePath } from './util';
|
import { monotonicTime, serializeError, sanitizeForFilePath } from './util';
|
||||||
import { TestBeginPayload, TestEndPayload, RunPayload, TestEntry, DonePayload, WorkerInitParams, StepBeginPayload, StepEndPayload } from './ipc';
|
import { TestBeginPayload, TestEndPayload, RunPayload, TestEntry, DonePayload, WorkerInitParams, StepBeginPayload, StepEndPayload } from './ipc';
|
||||||
import { setCurrentTestInfo } from './globals';
|
import { setCurrentTestInfo } from './globals';
|
||||||
import { Loader } from './loader';
|
import { Loader } from './loader';
|
||||||
|
|
@ -28,6 +28,7 @@ import { Modifier, Suite, TestCase } from './test';
|
||||||
import { Annotations, CompleteStepCallback, TestError, TestInfo, TestInfoImpl, WorkerInfo } from './types';
|
import { Annotations, CompleteStepCallback, TestError, TestInfo, TestInfoImpl, WorkerInfo } from './types';
|
||||||
import { ProjectImpl } from './project';
|
import { ProjectImpl } from './project';
|
||||||
import { FixturePool, FixtureRunner } from './fixtures';
|
import { FixturePool, FixtureRunner } from './fixtures';
|
||||||
|
import { DeadlineRunner, raceAgainstDeadline } from '../utils/async';
|
||||||
|
|
||||||
const removeFolderAsync = util.promisify(rimraf);
|
const removeFolderAsync = util.promisify(rimraf);
|
||||||
|
|
||||||
|
|
@ -59,7 +60,7 @@ export class WorkerRunner extends EventEmitter {
|
||||||
this._isStopped = true;
|
this._isStopped = true;
|
||||||
|
|
||||||
// Interrupt current action.
|
// Interrupt current action.
|
||||||
this._currentDeadlineRunner?.setDeadline(0);
|
this._currentDeadlineRunner?.interrupt();
|
||||||
|
|
||||||
// TODO: mark test as 'interrupted' instead.
|
// TODO: mark test as 'interrupted' instead.
|
||||||
if (this._currentTest && this._currentTest.testInfo.status === 'passed')
|
if (this._currentTest && this._currentTest.testInfo.status === 'passed')
|
||||||
|
|
@ -97,7 +98,7 @@ export class WorkerRunner extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _deadline() {
|
private _deadline() {
|
||||||
return this._project.config.timeout ? monotonicTime() + this._project.config.timeout : undefined;
|
return this._project.config.timeout ? monotonicTime() + this._project.config.timeout : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _loadIfNeeded() {
|
private async _loadIfNeeded() {
|
||||||
|
|
@ -263,7 +264,7 @@ export class WorkerRunner extends EventEmitter {
|
||||||
setTimeout: (timeout: number) => {
|
setTimeout: (timeout: number) => {
|
||||||
testInfo.timeout = timeout;
|
testInfo.timeout = timeout;
|
||||||
if (deadlineRunner)
|
if (deadlineRunner)
|
||||||
deadlineRunner.setDeadline(deadline());
|
deadlineRunner.updateDeadline(deadline());
|
||||||
},
|
},
|
||||||
_testFinished: new Promise(f => testFinishedCallback = f),
|
_testFinished: new Promise(f => testFinishedCallback = f),
|
||||||
_addStep: (category: string, title: string) => {
|
_addStep: (category: string, title: string) => {
|
||||||
|
|
@ -326,7 +327,7 @@ export class WorkerRunner extends EventEmitter {
|
||||||
setCurrentTestInfo(testInfo);
|
setCurrentTestInfo(testInfo);
|
||||||
|
|
||||||
const deadline = () => {
|
const deadline = () => {
|
||||||
return testInfo.timeout ? startTime + testInfo.timeout : undefined;
|
return testInfo.timeout ? startTime + testInfo.timeout : 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (reportEvents)
|
if (reportEvents)
|
||||||
|
|
@ -351,7 +352,7 @@ export class WorkerRunner extends EventEmitter {
|
||||||
|
|
||||||
if (!result.timedOut) {
|
if (!result.timedOut) {
|
||||||
this._currentDeadlineRunner = deadlineRunner = new DeadlineRunner(this._runAfterHooks(test, testInfo), deadline());
|
this._currentDeadlineRunner = deadlineRunner = new DeadlineRunner(this._runAfterHooks(test, testInfo), deadline());
|
||||||
deadlineRunner.setDeadline(deadline());
|
deadlineRunner.updateDeadline(deadline());
|
||||||
const hooksResult = await deadlineRunner.result;
|
const hooksResult = await deadlineRunner.result;
|
||||||
// Do not overwrite test failure upon hook timeout.
|
// Do not overwrite test failure upon hook timeout.
|
||||||
if (hooksResult.timedOut && testInfo.status === 'passed')
|
if (hooksResult.timedOut && testInfo.status === 'passed')
|
||||||
|
|
|
||||||
103
src/utils/async.ts
Normal file
103
src/utils/async.ts
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { monotonicTime } from './utils';
|
||||||
|
|
||||||
|
export class DeadlineRunner<T> {
|
||||||
|
private _timer: NodeJS.Timer | undefined;
|
||||||
|
readonly result = new ManualPromise<{ result?: T, timedOut?: boolean }>();
|
||||||
|
|
||||||
|
constructor(promise: Promise<T>, deadline: number) {
|
||||||
|
promise.then(result => {
|
||||||
|
this._finish({ result });
|
||||||
|
}).catch(e => {
|
||||||
|
this._finish(undefined, e);
|
||||||
|
});
|
||||||
|
this.updateDeadline(deadline);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _finish(success?: { result?: T, timedOut?: boolean }, error?: any) {
|
||||||
|
if (this.result.isDone())
|
||||||
|
return;
|
||||||
|
this.updateDeadline(0);
|
||||||
|
if (success)
|
||||||
|
this.result.resolve(success);
|
||||||
|
else
|
||||||
|
this.result.reject(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
interrupt() {
|
||||||
|
this.updateDeadline(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDeadline(deadline: number) {
|
||||||
|
if (this._timer) {
|
||||||
|
clearTimeout(this._timer);
|
||||||
|
this._timer = undefined;
|
||||||
|
}
|
||||||
|
if (deadline === 0)
|
||||||
|
return;
|
||||||
|
const timeout = deadline - monotonicTime();
|
||||||
|
if (timeout <= 0)
|
||||||
|
this._finish({ timedOut: true });
|
||||||
|
else
|
||||||
|
this._timer = setTimeout(() => this._finish({ timedOut: true }), timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function raceAgainstDeadline<T>(promise: Promise<T>, deadline: number): Promise<{ result?: T, timedOut?: boolean }> {
|
||||||
|
return (new DeadlineRunner(promise, deadline)).result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ManualPromise<T> extends Promise<T> {
|
||||||
|
private _resolve!: (t: T) => void;
|
||||||
|
private _reject!: (e: Error) => void;
|
||||||
|
private _isDone: boolean;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
let resolve: (t: T) => void;
|
||||||
|
let reject: (e: Error) => void;
|
||||||
|
super((f, r) => {
|
||||||
|
resolve = f;
|
||||||
|
reject = r;
|
||||||
|
});
|
||||||
|
this._isDone = false;
|
||||||
|
this._resolve = resolve!;
|
||||||
|
this._reject = reject!;
|
||||||
|
}
|
||||||
|
|
||||||
|
isDone() {
|
||||||
|
return this._isDone;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(t: T) {
|
||||||
|
this._isDone = true;
|
||||||
|
this._resolve(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
reject(e: Error) {
|
||||||
|
this._isDone = true;
|
||||||
|
this._reject(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
static override get [Symbol.species]() {
|
||||||
|
return Promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
override get [Symbol.toStringTag]() {
|
||||||
|
return 'ManualPromise';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -392,43 +392,3 @@ export function wrapInASCIIBox(text: string, padding = 0): string {
|
||||||
'╚' + '═'.repeat(maxLength + padding * 2) + '╝',
|
'╚' + '═'.repeat(maxLength + padding * 2) + '╝',
|
||||||
].join('\n');
|
].join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ManualPromise<T> extends Promise<T> {
|
|
||||||
private _resolve!: (t: T) => void;
|
|
||||||
private _reject!: (e: Error) => void;
|
|
||||||
private _isDone: boolean;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
let resolve: (t: T) => void;
|
|
||||||
let reject: (e: Error) => void;
|
|
||||||
super((f, r) => {
|
|
||||||
resolve = f;
|
|
||||||
reject = r;
|
|
||||||
});
|
|
||||||
this._isDone = false;
|
|
||||||
this._resolve = resolve!;
|
|
||||||
this._reject = reject!;
|
|
||||||
}
|
|
||||||
|
|
||||||
isDone() {
|
|
||||||
return this._isDone;
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(t: T) {
|
|
||||||
this._isDone = true;
|
|
||||||
this._resolve(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
reject(e: Error) {
|
|
||||||
this._isDone = true;
|
|
||||||
this._reject(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
static override get [Symbol.species]() {
|
|
||||||
return Promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
override get [Symbol.toStringTag]() {
|
|
||||||
return 'ManualPromise';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue