UNPKG

poll-until-promise

Version:

Try repeatedly for a promise to be resolved

164 lines (163 loc) 6.37 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.waitFor = exports.PollUntil = void 0; const abort_1 = require("./abort"); const ERRORS = { NOT_FUNCTION: 'Your executor is not a function. functions and promises are valid.', FAILED_TO_WAIT: 'Failed to wait', }; function promisify(fn) { return () => __awaiter(this, void 0, void 0, function* () { const result = yield fn(); return result; }); } function validateExecution(executeFn) { if (typeof executeFn !== 'function') { throw new Error(ERRORS.NOT_FUNCTION); } } class PollUntil { constructor({ interval = 100, timeout = 1000, stopOnFailure = false, verbose = false, backoffFactor = 1, backoffMaxInterval, message = '', maxAttempts, } = {}) { this._interval = interval; this._timeout = timeout; this._executedAttempts = 0; this._stopOnFailure = stopOnFailure; this._isWaiting = false; this._isResolved = false; this._verbose = verbose; this._userMessage = message; this.originalStacktraceError = new Error(); this._Console = console; this._backoffFactor = backoffFactor; this._backoffMaxInterval = backoffMaxInterval || timeout; this._maxAttempts = maxAttempts; this.start = +Date.now(); } tryEvery(interval) { this._interval = interval; return this; } stopAfter(timeout) { this._timeout = timeout; return this; } execute(executeFn) { this._applyPromiseHandlers(); validateExecution(executeFn); this._executeFn = promisify(executeFn); this.start = Date.now(); this._isWaiting = true; this._log('starting to execute'); this._runFunction(); return this.promise; } getPromise() { return this.promise; } isResolved() { return this._isResolved; } isWaiting() { return this._isWaiting; } stopOnFailure(stop) { this._stopOnFailure = stop; return this; } _applyPromiseHandlers() { this.promise = new Promise((resolve, reject) => { this.resolve = resolve; this.reject = reject; }); } _timeFromStart() { return Date.now() - this.start; } _shouldStopTrying() { return this._timeFromStart() > this._timeout || this._attemptsExhausted(); } _attemptsExhausted() { return this._maxAttempts !== undefined && this._executedAttempts >= this._maxAttempts; } _executeAgain() { this._log('executing again'); const currentInterval = this._interval; const nextInterval = currentInterval * this._backoffFactor; this._interval = (nextInterval > this._backoffMaxInterval) ? this._backoffMaxInterval : nextInterval; this._executedAttempts += 1; setTimeout(this._runFunction.bind(this), currentInterval); } _failedToWait() { const timeFromStartStr = `${this._timeFromStart()}ms`; let waitErrorText = this._attemptsExhausted() ? `Operation unsuccessful after ${this._executedAttempts} attempts (total of ${timeFromStartStr})` : `${ERRORS.FAILED_TO_WAIT} after ${timeFromStartStr} (total of ${this._executedAttempts} attempts)`; if (this._userMessage) waitErrorText = `${waitErrorText}: ${this._userMessage}`; if (this._lastError) { this._lastError.message = `${waitErrorText}\n${this._lastError.message}`; const originalStack = this.originalStacktraceError.stack; if (originalStack) { this._lastError.stack += originalStack.substring(originalStack.indexOf('\n') + 1); } } else { this._lastError = this.originalStacktraceError; this._lastError.message = waitErrorText; } this._log(this._lastError); return this._lastError; } _runFunction() { var _a; if (this._shouldStopTrying()) { this._isWaiting = false; (_a = this.reject) === null || _a === void 0 ? void 0 : _a.call(this, this._failedToWait()); return; } this._executeFn() .then((result) => { var _a; if (result === false) { this._log(`then execute again with result: ${result}`); this._executeAgain(); return; } (_a = this.resolve) === null || _a === void 0 ? void 0 : _a.call(this, result); this._isWaiting = false; this._isResolved = true; this._log(`then done waiting with result: ${result}`); }) .catch((err) => { var _a, _b; if (err instanceof abort_1.AbortError) { this._log(`aborted with err: ${err.cause}`); return (_a = this.reject) === null || _a === void 0 ? void 0 : _a.call(this, err.cause); } if (this._stopOnFailure) { this._log(`stopped on failure with err: ${err}`); return (_b = this.reject) === null || _b === void 0 ? void 0 : _b.call(this, err); } this._lastError = err; this._log(`catch with err: ${err}`); return this._executeAgain(); }); } _log(message) { if (this._verbose && this._Console && this._Console.log) this._Console.log(message); } } exports.PollUntil = PollUntil; const waitFor = (waitForFunction, options) => new PollUntil(options).execute(waitForFunction); exports.waitFor = waitFor;