UNPKG

ran-boilerplate

Version:

React . Apollo (GraphQL) . Next.js Toolkit

221 lines (219 loc) 7.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); /** * Helper to make a Subscribe function (just like Promise helps make a * Thenable). * * @param executor Function which can make calls to a single Observer * as a proxy. * @param onNoObservers Callback when count of Observers goes to zero. */ function createSubscribe(executor, onNoObservers) { var proxy = new ObserverProxy(executor, onNoObservers); return proxy.subscribe.bind(proxy); } exports.createSubscribe = createSubscribe; /** * Implement fan-out for any number of Observers attached via a subscribe * function. */ var ObserverProxy = /** @class */ (function () { /** * @param executor Function which can make calls to a single Observer * as a proxy. * @param onNoObservers Callback when count of Observers goes to zero. */ function ObserverProxy(executor, onNoObservers) { var _this = this; this.observers = []; this.unsubscribes = []; this.observerCount = 0; // Micro-task scheduling by calling task.then(). this.task = Promise.resolve(); this.finalized = false; this.onNoObservers = onNoObservers; // Call the executor asynchronously so subscribers that are called // synchronously after the creation of the subscribe function // can still receive the very first value generated in the executor. this.task .then(function () { executor(_this); }) .catch(function (e) { _this.error(e); }); } ObserverProxy.prototype.next = function (value) { this.forEachObserver(function (observer) { observer.next(value); }); }; ObserverProxy.prototype.error = function (error) { this.forEachObserver(function (observer) { observer.error(error); }); this.close(error); }; ObserverProxy.prototype.complete = function () { this.forEachObserver(function (observer) { observer.complete(); }); this.close(); }; /** * Subscribe function that can be used to add an Observer to the fan-out list. * * - We require that no event is sent to a subscriber sychronously to their * call to subscribe(). */ ObserverProxy.prototype.subscribe = function (nextOrObserver, error, complete) { var _this = this; var observer; if (nextOrObserver === undefined && error === undefined && complete === undefined) { throw new Error('Missing Observer.'); } // Assemble an Observer object when passed as callback functions. if (implementsAnyMethods(nextOrObserver, ['next', 'error', 'complete'])) { observer = nextOrObserver; } else { observer = { next: nextOrObserver, error: error, complete: complete }; } if (observer.next === undefined) { observer.next = noop; } if (observer.error === undefined) { observer.error = noop; } if (observer.complete === undefined) { observer.complete = noop; } var unsub = this.unsubscribeOne.bind(this, this.observers.length); // Attempt to subscribe to a terminated Observable - we // just respond to the Observer with the final error or complete // event. if (this.finalized) { this.task.then(function () { try { if (_this.finalError) { observer.error(_this.finalError); } else { observer.complete(); } } catch (e) { // nothing } return; }); } this.observers.push(observer); return unsub; }; // Unsubscribe is synchronous - we guarantee that no events are sent to // any unsubscribed Observer. ObserverProxy.prototype.unsubscribeOne = function (i) { if (this.observers === undefined || this.observers[i] === undefined) { return; } delete this.observers[i]; this.observerCount -= 1; if (this.observerCount === 0 && this.onNoObservers !== undefined) { this.onNoObservers(this); } }; ObserverProxy.prototype.forEachObserver = function (fn) { if (this.finalized) { // Already closed by previous event....just eat the additional values. return; } // Since sendOne calls asynchronously - there is no chance that // this.observers will become undefined. for (var i = 0; i < this.observers.length; i++) { this.sendOne(i, fn); } }; // Call the Observer via one of it's callback function. We are careful to // confirm that the observe has not been unsubscribed since this asynchronous // function had been queued. ObserverProxy.prototype.sendOne = function (i, fn) { var _this = this; // Execute the callback asynchronously this.task.then(function () { if (_this.observers !== undefined && _this.observers[i] !== undefined) { try { fn(_this.observers[i]); } catch (e) { // Ignore exceptions raised in Observers or missing methods of an // Observer. // Log error to console. b/31404806 if (typeof console !== 'undefined' && console.error) { console.error(e); } } } }); }; ObserverProxy.prototype.close = function (err) { var _this = this; if (this.finalized) { return; } this.finalized = true; if (err !== undefined) { this.finalError = err; } // Proxy is no longer needed - garbage collect references this.task.then(function () { _this.observers = undefined; _this.onNoObservers = undefined; }); }; return ObserverProxy; }()); /** Turn synchronous function into one called asynchronously. */ function async(fn, onError) { return function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } Promise.resolve(true) .then(function () { fn.apply(void 0, args); }) .catch(function (error) { if (onError) { onError(error); } }); }; } exports.async = async; /** * Return true if the object passed in implements any of the named methods. */ function implementsAnyMethods(obj, methods) { if (typeof obj !== 'object' || obj === null) { return false; } for (var _i = 0, methods_1 = methods; _i < methods_1.length; _i++) { var method = methods_1[_i]; if (method in obj && typeof obj[method] === 'function') { return true; } } return false; } function noop() { // do nothing } //# sourceMappingURL=subscribe.js.map