Home Reference Source

src/utils/Throttler.js

/**

 @author: Tom Clement (tjclement)
 @license NPOSL-3.0
 @copyright Bizboard, 2015

 */

import Timer                            from 'famous/utilities/Timer.js';
import {ObjectHelper}                   from './ObjectHelper.js';

export class Throttler {
    /**
     *
     * @param {Number} throttleDelay Minimum amount of time in between each action executed by the Throttler, in milliseconds or ticks.
     * @param {Boolean} shouldQueue Enable if each added action should be executed consecutively, or disable if a newly
     * added action should replace a previous one.
     * @param {Object} actionContext Context to which the actions executed by the Throttler will be bound.
     * @param {Boolean} useTicks whether ticks should be used instead of milliseconds
     * @returns {Throttler} Throttler instance.
     */
    constructor(throttleDelay = 0, shouldQueue = true, actionContext = this, useTicks = false) {
        this.delay = throttleDelay;
        this._useTicks = useTicks;
        this._timerFunction = useTicks ? Timer.every : Timer.setInterval;
        this.timer = null;
        this.shouldQueue = shouldQueue;
        this.actionContext = actionContext;

        this.queue = [];
        this.executionTimer = null;

        ObjectHelper.bindAllMethods(this, this);
    }

    /**
     * Adds an executable action to the queue that will be executed consecutively by the Throttler.
     * If Throttler was constructed with shouldQueue = false, adding a new action will remove the old one.
     * @param {Function} action Function to execute.
     * @returns {void}
     */
    add(action) {
        /* If we're not queueing, clear the previous action if present. The new action will replace the old one. */
        if (!this.shouldQueue) {
            this.queue.pop();
        }

        this.queue.push(action);
        if (!this.timer) {
            this.timer = this._timerFunction(this._executeTopAction, this.delay);
        }
    }

    /**
     * Clears the Throttler's timer if it is set.
     * @returns {void}
     * @private
     */
    _clearTimer() {
        Timer.clear(this.timer);
        this.timer = null;
    }

    /**
     * Removes the top action from the Throttler's queue if any is present, and executes it with the correct binding context.
     * @returns {void}
     * @private
     */
    _executeTopAction() {
        let action = this.queue.shift();
        if (action && typeof action === 'function') {
            action.call(this.actionContext);
        }
        if (!this.queue.length) {
            this._clearTimer();
        }
    }
}