UNPKG

threads

Version:

Web workers & worker threads as simple as a function call

147 lines (146 loc) 5.14 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ObservablePromise = void 0; const observable_fns_1 = require("observable-fns"); const doNothing = () => undefined; const returnInput = (input) => input; const runDeferred = (fn) => Promise.resolve().then(fn); function fail(error) { throw error; } function isThenable(thing) { return thing && typeof thing.then === "function"; } /** * Creates a hybrid, combining the APIs of an Observable and a Promise. * * It is used to proxy async process states when we are initially not sure * if that async process will yield values once (-> Promise) or multiple * times (-> Observable). * * Note that the observable promise inherits some of the observable's characteristics: * The `init` function will be called *once for every time anyone subscribes to it*. * * If this is undesired, derive a hot observable from it using `makeHot()` and * subscribe to that. */ class ObservablePromise extends observable_fns_1.Observable { constructor(init) { super((originalObserver) => { // tslint:disable-next-line no-this-assignment const self = this; const observer = Object.assign(Object.assign({}, originalObserver), { complete() { originalObserver.complete(); self.onCompletion(); }, error(error) { originalObserver.error(error); self.onError(error); }, next(value) { originalObserver.next(value); self.onNext(value); } }); try { this.initHasRun = true; return init(observer); } catch (error) { observer.error(error); } }); this.initHasRun = false; this.fulfillmentCallbacks = []; this.rejectionCallbacks = []; this.firstValueSet = false; this.state = "pending"; } onNext(value) { if (!this.firstValueSet) { this.firstValue = value; this.firstValueSet = true; } } onError(error) { this.state = "rejected"; this.rejection = error; for (const onRejected of this.rejectionCallbacks) { // Promisifying the call to turn errors into unhandled promise rejections // instead of them failing sync and cancelling the iteration runDeferred(() => onRejected(error)); } } onCompletion() { this.state = "fulfilled"; for (const onFulfilled of this.fulfillmentCallbacks) { // Promisifying the call to turn errors into unhandled promise rejections // instead of them failing sync and cancelling the iteration runDeferred(() => onFulfilled(this.firstValue)); } } then(onFulfilledRaw, onRejectedRaw) { const onFulfilled = onFulfilledRaw || returnInput; const onRejected = onRejectedRaw || fail; let onRejectedCalled = false; return new Promise((resolve, reject) => { const rejectionCallback = (error) => { if (onRejectedCalled) return; onRejectedCalled = true; try { resolve(onRejected(error)); } catch (anotherError) { reject(anotherError); } }; const fulfillmentCallback = (value) => { try { resolve(onFulfilled(value)); } catch (error) { rejectionCallback(error); } }; if (!this.initHasRun) { this.subscribe({ error: rejectionCallback }); } if (this.state === "fulfilled") { return resolve(onFulfilled(this.firstValue)); } if (this.state === "rejected") { onRejectedCalled = true; return resolve(onRejected(this.rejection)); } this.fulfillmentCallbacks.push(fulfillmentCallback); this.rejectionCallbacks.push(rejectionCallback); }); } catch(onRejected) { return this.then(undefined, onRejected); } finally(onCompleted) { const handler = onCompleted || doNothing; return this.then((value) => { handler(); return value; }, () => handler()); } static from(thing) { if (isThenable(thing)) { return new ObservablePromise(observer => { const onFulfilled = (value) => { observer.next(value); observer.complete(); }; const onRejected = (error) => { observer.error(error); }; thing.then(onFulfilled, onRejected); }); } else { return super.from(thing); } } } exports.ObservablePromise = ObservablePromise;