UNPKG

@oat-sa/tao-core-sdk

Version:
365 lines (331 loc) 11.2 kB
define(['lodash', 'core/promise', 'core/eventifier'], function (_, Promise, eventifier) { 'use strict'; _ = _ && Object.prototype.hasOwnProperty.call(_, 'default') ? _['default'] : _; Promise = Promise && Object.prototype.hasOwnProperty.call(Promise, 'default') ? Promise['default'] : Promise; eventifier = eventifier && Object.prototype.hasOwnProperty.call(eventifier, 'default') ? eventifier['default'] : eventifier; /** * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; under version 2 * of the License (non-upgradable). * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Copyright (c) 2015-2019 (original work) Open Assessment Technologies SA ; */ /** * The default value of the polling interval * @type {Number} * @private */ const _defaultInterval = 60 * 1000; /** * Create a polling manager for a particular action * @param {Object|Function} [config] - A config object, or the action called on each iteration * @param {Function} [config.action] - The callback action called on each iteration, the polling instance is provided as first argument * @param {Number|String} [config.interval] - The minimal time between two iterations * @param {Number|String} [config.max] - Set a max number of iterations, after what the polling is stopped. * @param {Boolean} [config.autoStart] - Whether or not the polling should start immediately * @param {Object} [config.context] - An optional context to apply on each action call * @param {number} pollingInterval - The minimal time between two iterations (to be set when the first parameter is a function) * @returns {polling} */ function pollingFactory(config) { let pollingInterval = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _defaultInterval; let timer, promise, interval, max, iter, action, context, autoStart; const state = {}; /** * Defines the polling manager * @type {Object} */ const polling = { /** * Gets the current action into asynchronous mode. * The next iteration won't be executed until the resolve method has been called. * However if the reject method is called, the polling is then stopped! * @returns {Object} Returns a promise resolver that provides resolve() and reject() methods */ async() { const resolver = {}; // create a promise and extract the control callbacks promise = new Promise(function (resolve, reject) { resolver.resolve = resolve; resolver.reject = reject; }); // directly install the pending actions promise.then(function () { promise = null; state.processing = false; // next iteration only if allowed if (!state.stopped) { startTimer(); } /** * Notifies the polling continues * @event polling#resolved */ polling.trigger('resolved'); }).catch(function () { promise = null; state.processing = false; // breaks the polling polling.stop(); /** * Notifies the polling has been halted * @event polling#rejected */ polling.trigger('rejected'); }); /** * Notifies the current action is asynchronous * @event polling#async * @param {Object} resolver * @param {Function} resolver.resolve * @param {Function} resolver.reject */ polling.trigger('async', resolver); return resolver; }, /** * Forces the next iteration to be executed now, unless it is already running. * If the polling has been stopped, start it again. * @returns {polling} */ next() { var _next; // reset the counter if the polling is stopped if (state.stopped) { iter = 0; } // ensure the scheduling if off stopTimer(); // prevent more iterations than needed to be ran if (max && iter >= max) { return this; } // the next() method can be called either to force a next iteration or to start immediately the action // so we need to ensure the schedule is not blocked state.stopped = false; if (!promise) { /** * Notifies the action * @event polling#next * @param {polling} polling */ this.trigger('next'); iteration(); } else { // as a promise is still pending, ensure a call to next() will be processed after _next = this.next.bind(this); promise.then(_next).catch(_next); } return this; }, /** * Starts the polling if it is not currently running * @returns {polling} */ start() { if (!timer) { iter = 0; startTimer(); /** * Notifies the start * @event polling#start */ this.trigger('start'); } return this; }, /** * Stops the polling if it is currently running * @returns {polling} */ stop() { stopTimer(); /** * Notifies the stop * @event polling#stop */ this.trigger('stop'); return this; }, /** * Sets the minimum time interval between two actions * @param {Number|String} value * @returns {polling} */ setInterval(value) { interval = Math.abs(parseInt(value, 10) || _defaultInterval); /** * Notifies the interval change * @event polling#setinterval * @param {Number} interval */ this.trigger('setinterval', interval); return this; }, /** * Gets the minimum time interval between two actions * @returns {Number} */ getInterval() { return interval; }, /** * Sets the polling action * @param {Function} fn * @returns {polling} */ setAction(fn) { action = fn; /** * Notifies the action change * @event polling#setaction * @param {Function} action */ this.trigger('setaction', action); return this; }, /** * Gets the polling action * @returns {Function} */ getAction() { return action; }, /** * Sets the context applied on each action call * @param {Object} ctx * @returns {polling} */ setContext(ctx) { context = ctx || this; /** * Notifies the context change * @event polling#setcontext * @param {Object} context */ this.trigger('setcontext', ctx); return this; }, /** * Gets the context applied on each action call * @returns {Object} */ getContext() { return context; }, /** * Sets the max number of polling occurrences * @param {Number} value * @returns {polling} */ setMax(value) { max = Math.abs(parseInt(value, 10) || 0); return this; }, /** * Gets the max number of polling occurrences * @returns {Number} */ getMax() { return max; }, /** * Gets the number of ran iterations * @returns {Number} */ getIteration() { return iter || 0; }, /** * Checks if the manager is in a particular state * @param {String} stateName The name of the state to check. Possible values are: * - stopped: the polling manager is stopped, and won't process action until restart * - pending: the polling manager has scheduled an action an is waiting for it processing * - processing: the polling manager is currently processing an action and wait for its completion * @returns {Boolean} */ is: function is(stateName) { return !!state[stateName]; } }; /** * Fires a new timer */ function startTimer() { timer = setTimeout(iteration, interval); state.stopped = false; state.pending = true; } /** * Stops the current timer */ function stopTimer() { clearTimeout(timer); timer = null; state.stopped = true; state.pending = false; } /** * Runs an iteration of the polling loop */ function iteration() { // prevent more iterations than needed to be ran if (max && iter >= max) { // breaks the polling polling.stop(); return; } // count the iteration iter = (iter || 0) + 1; state.processing = true; state.pending = false; /** * Notifies the action is about to be called * @event polling#call */ polling.trigger('call'); // process the action in the right context action.call(context, polling); // next iteration in synchronous mode if (!promise && !state.stopped) { state.processing = false; startTimer(); } } eventifier(polling); // some defaults interval = _defaultInterval; context = polling; action = null; state.stopped = true; autoStart = false; iter = 0; // maybe only the action is provided if (_.isFunction(config)) { polling.setAction(config); config = null; } // loads the config if (_.isObject(config)) { polling.setAction(config.action); polling.setInterval(config.interval || pollingInterval); polling.setContext(config.context); polling.setMax(config.max); autoStart = !!config.autoStart; } if (autoStart) { polling.start(); } return polling; } return pollingFactory; });