UNPKG

apollo-client

Version:
146 lines 7.2 kB
// The QueryScheduler is supposed to be a mechanism that schedules polling queries such that // they are clustered into the time slots of the QueryBatcher and are batched together. It // also makes sure that for a given polling query, if one instance of the query is inflight, // another instance will not be fired until the query returns or times out. We do this because // another query fires while one is already in flight, the data will stay in the "loading" state // even after the first query has returned. var __assign = (this && this.__assign) || Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; import { FetchType } from '../core/types'; import { ObservableQuery } from '../core/ObservableQuery'; import { NetworkStatus } from '../core/networkStatus'; var QueryScheduler = /** @class */ (function () { function QueryScheduler(_a) { var queryManager = _a.queryManager, ssrMode = _a.ssrMode; // Map going from queryIds to query options that are in flight. this.inFlightQueries = {}; // Map going from query ids to the query options associated with those queries. Contains all of // the queries, both in flight and not in flight. this.registeredQueries = {}; // Map going from polling interval with to the query ids that fire on that interval. // These query ids are associated with a set of options in the this.registeredQueries. this.intervalQueries = {}; // Map going from polling interval widths to polling timers. this.pollingTimers = {}; this.ssrMode = false; this.queryManager = queryManager; this.ssrMode = ssrMode || false; } QueryScheduler.prototype.checkInFlight = function (queryId) { var query = this.queryManager.queryStore.get(queryId); return (query && query.networkStatus !== NetworkStatus.ready && query.networkStatus !== NetworkStatus.error); }; QueryScheduler.prototype.fetchQuery = function (queryId, options, fetchType) { var _this = this; return new Promise(function (resolve, reject) { _this.queryManager .fetchQuery(queryId, options, fetchType) .then(function (result) { resolve(result); }) .catch(function (error) { reject(error); }); }); }; QueryScheduler.prototype.startPollingQuery = function (options, queryId, listener) { if (!options.pollInterval) { throw new Error('Attempted to start a polling query without a polling interval.'); } // Do not poll in SSR mode if (this.ssrMode) return queryId; this.registeredQueries[queryId] = options; if (listener) { this.queryManager.addQueryListener(queryId, listener); } this.addQueryOnInterval(queryId, options); return queryId; }; QueryScheduler.prototype.stopPollingQuery = function (queryId) { // Remove the query options from one of the registered queries. // The polling function will then take care of not firing it anymore. delete this.registeredQueries[queryId]; }; // Fires the all of the queries on a particular interval. Called on a setInterval. QueryScheduler.prototype.fetchQueriesOnInterval = function (interval) { var _this = this; // XXX this "filter" here is nasty, because it does two things at the same time. // 1. remove queries that have stopped polling // 2. call fetchQueries for queries that are polling and not in flight. // TODO: refactor this to make it cleaner this.intervalQueries[interval] = this.intervalQueries[interval].filter(function (queryId) { // If queryOptions can't be found from registeredQueries or if it has a // different interval, it means that this queryId is no longer registered // and should be removed from the list of queries firing on this interval. // // We don't remove queries from intervalQueries immediately in // stopPollingQuery so that we can keep the timer consistent when queries // are removed and replaced, and to avoid quadratic behavior when stopping // many queries. if (!(_this.registeredQueries.hasOwnProperty(queryId) && _this.registeredQueries[queryId].pollInterval === interval)) { return false; } // Don't fire this instance of the polling query is one of the instances is already in // flight. if (_this.checkInFlight(queryId)) { return true; } var queryOptions = _this.registeredQueries[queryId]; var pollingOptions = __assign({}, queryOptions); pollingOptions.fetchPolicy = 'network-only'; // don't let unhandled rejections happen _this.fetchQuery(queryId, pollingOptions, FetchType.poll).catch(function () { }); return true; }); if (this.intervalQueries[interval].length === 0) { clearInterval(this.pollingTimers[interval]); delete this.intervalQueries[interval]; } }; // Adds a query on a particular interval to this.intervalQueries and then fires // that query with all the other queries executing on that interval. Note that the query id // and query options must have been added to this.registeredQueries before this function is called. QueryScheduler.prototype.addQueryOnInterval = function (queryId, queryOptions) { var _this = this; var interval = queryOptions.pollInterval; if (!interval) { throw new Error("A poll interval is required to start polling query with id '" + queryId + "'."); } // If there are other queries on this interval, this query will just fire with those // and we don't need to create a new timer. if (this.intervalQueries.hasOwnProperty(interval.toString()) && this.intervalQueries[interval].length > 0) { this.intervalQueries[interval].push(queryId); } else { this.intervalQueries[interval] = [queryId]; // set up the timer for the function that will handle this interval this.pollingTimers[interval] = setInterval(function () { _this.fetchQueriesOnInterval(interval); }, interval); } }; // Used only for unit testing. QueryScheduler.prototype.registerPollingQuery = function (queryOptions) { if (!queryOptions.pollInterval) { throw new Error('Attempted to register a non-polling query with the scheduler.'); } return new ObservableQuery({ scheduler: this, options: queryOptions, }); }; return QueryScheduler; }()); export { QueryScheduler }; //# sourceMappingURL=scheduler.js.map