slowpoke

Декоратор для функции action, не выполняющий ее чаще limit раз в секунду

by Valentin Sarychev

HTML

<link rel="stylesheet" href="http://jasmine.github.io/2.1/lib/jasmine.css">
<script src="http://jasmine.github.io/2.1/lib/jasmine.js"></script>
<script src="http://jasmine.github.io/2.1/lib/jasmine-html.js"></script>
<script src="http://jasmine.github.io/2.1/lib/boot.js"></script>

JavaScript

// Декоратор для функции action, не выполняющий ее чаще limit раз в секунду
function slowpoke(limit, action) {
    var timeout = 1000 / limit;
    var timer = null;
    var self, args;

    // Выполняет action, если требуется
    function tryInvoke() {
        // остановить таймер, если нечего выполнять
        if (!args) {
            clearInterval(timer);
            timer = null;
            return;
        }

        // выполнить action
        action.apply(self, args);

        // сбросить параметры вызова, чтобы освободить память и показать, что нечего выполнять
        self = args = null;
    }

    return function () {
        self = this;
        args = Array.prototype.slice.call(arguments);

        // если таймер запущен, поручить выполнение action ему
        if (timer !== null) {
            return;
        }

        // если нет, запустить таймер
        timer = setInterval(tryInvoke, timeout);

        // и выполнить action сейчас
        tryInvoke();
    };
}

// ================ тесты ================

describe('slowpoke', function () {
    var originAction;
    var slowpokeAction;
    var n = 10;

    beforeEach(function () {
        jasmine.clock().install();
        originAction = jasmine.createSpy('action');
        slowpokeAction = slowpoke(n, originAction);
    });
    
    afterEach(function() {
        jasmine.clock().uninstall();
    });

    describe('возвращает функцию, которая выполняется', function () {
        it('синхронно при первом вызове', function () {
            slowpokeAction();
            expect(originAction.calls.count()).toBe(1);
        });

        it('дважды с интервалом 1/n секунд при 3х вызовах подряд', function () {
            slowpokeAction();
            slowpokeAction();
            slowpokeAction();

            expect(originAction.calls.count()).toBe(1);

            jasmine.clock().tick(1000 / n - 1);
            expect(originAction.calls.count()).toBe(1);

            jasmine.clock().tick(1);
           ...