UNPKG

@tsdotnet/promises

Version:

An extended A+ promise library with lazy and synchronous promises.

142 lines 5.61 kB
"use strict"; /*! * @author electricessence / https://github.com/electricessence/ * Licensing: MIT */ Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const ArgumentNullException_1 = tslib_1.__importDefault(require("@tsdotnet/exceptions/dist/ArgumentNullException")); const defer_1 = tslib_1.__importDefault(require("@tsdotnet/threading/dist/defer")); const Promise_1 = require("./Promise"); const VOID0 = void 0; /** * A promise that waits for the first then to trigger the resolver. */ class LazyPromise extends Promise_1.TSDNPromise { constructor(_resolver) { super(); this._resolver = _resolver; if (!_resolver) throw new ArgumentNullException_1.default('resolver'); this._resolvedCalled = true; } thenSynchronous(onFulfilled, onRejected) { this._onThen(); return super.thenSynchronous(onFulfilled, onRejected); } doneNow(onFulfilled, onRejected) { this._onThen(); super.doneNow(onFulfilled, onRejected); } /** * Will yield for a number of milliseconds from the time called before continuing. * @param milliseconds * @returns A promise that yields to the current execution and executes after a minimum delay. */ delayFromNow(milliseconds = 0) { this.throwIfDisposed(); // If this is already guaranteed to resolve, the go ahead and pass to the super. if (!this._resolver || this.isSettled) return super.delayFromNow(milliseconds); /* * If not triggered yet, then we create a special promise * that only requests the resolution from the parent promise * if a 'then' is called to ensure the lazy pattern. */ let pass; let timedOut = false; // Setup the timer. let timeout = (0, defer_1.default)(() => { timedOut = true; // If the promise was requested already go ahead and pass the request on to the parent. if (pass) pass(); }, milliseconds); return new LazyPromise((resolve, reject) => { // A lazy promise only enters here if something called for a resolution. pass = () => { this.doneNow(v => resolve(v), e => reject(e)); timeout.dispose(); timeout = VOID0; pass = VOID0; }; // If the timeout completed already go ahead and pass the request on to the parent. if (timedOut) pass(); // Otherwise wait for the timeout to do it. }); } /** * Will yield for a number of milliseconds from after this promise resolves. * If the promise is already resolved, the delay will start from now. * @param milliseconds * @returns A promise that yields to the current execution and executes after a delay. */ delayAfterResolve(milliseconds = 0) { this.throwIfDisposed(); // If this is already guaranteed to resolve, the go ahead and pass to the super. if (!this._resolver || this.isSettled) return super.delayAfterResolve(milliseconds); /* * If not triggered yet, then we create a special promise * that only requests the resolution from the parent promise * if a 'then' is called to ensure the lazy pattern. */ let pass; // Setup the timer. let timeout; let finalize = () => { if (timeout) { timeout.dispose(); timeout = VOID0; } // If the promise was requested already go ahead and pass the request on to the parent. if (pass) pass(); finalize = VOID0; }; { let detector = () => { if (finalize) // We may already be wrapped up so never mind! timeout = (0, defer_1.default)(finalize, milliseconds); }; // Calling super.doneNow does not trigger resolution. // This simply waits for resolution to happen. // Is effectively the timer by when resolution has occurred. super.doneNow(detector, detector); //noinspection JSUnusedAssignment detector = null; } return new LazyPromise((resolve, reject) => { // Because of the lazy nature of this promise, this could enter here at any time. if (this.isPending) { this.doneNow(v => (0, defer_1.default)(() => resolve(v), milliseconds), e => (0, defer_1.default)(() => reject(e), milliseconds)); finalize(); } else { // We don't know when this resolved and could have happened anytime after calling this delay method. pass = () => { this.doneNow(v => resolve(v), e => reject(e)); }; // Already finalized (aka resolved after a timeout)? Go now! if (!finalize) pass(); } }); } // NOTE: For a LazyPromise we need to be careful not to trigger the resolve for delay. _onDispose() { super._onDispose(); this._resolver = VOID0; } _onThen() { const r = this._resolver; if (r) { this._resolver = VOID0; this._resolvedCalled = false; this.resolveUsing(r); } } } exports.default = LazyPromise; //# sourceMappingURL=LazyPromise.js.map