ilyachalov (ilyachalov) wrote,
ilyachalov
ilyachalov

Categories:

JavaScript, Sinon.JS, поддельные таймеры

Разобрался с вопросом в комментариях к подразделу 6.9 «Декораторы и переадресация вызова, call/apply» учебника по JavaScript:

https://learn.javascript.ru/call-apply-decorators#comment-5334632652

Автор комментария (с забавным ником Defeated Sanity) предложил следующее решение задачи «Декоратор debounce», о которой я писал в прошлом посте:

function debounce(f, ms) {
    let t = ms;
    function call () {
        if (Date.now() - call.time > t) {
            call.time = Date.now();
            f.apply(this, arguments);
        }
    }
    call.time = 0;
    return call;
}

Отмечу, что переменная t здесь лишняя, а присвоение call.time = Date.now(); правильнее поставить после вызова f.apply(this, arguments); (я разбирал этот момент в прошлом посте), но речь сейчас не об этом. Моё решение этой задачи из прошлого поста принципиально похоже на это решение, однако моё решение проходит песочницу с тестами авторов учебника, а вышеприведенное решение — не проходит первый тест из двух.

Для прохождения этого первого теста нужно, чтобы через декоратор прошли только первый и четвертый вызовы функций из тестового кода, заданного в условиях задачи.

Оказалось, что в песочнице через вышеприведенную функцию-декоратор проходит только четвертый вызов функции из тестового кода, а первый не проходит. При этом эта же функция-декоратор работает правильно, если ее запустить из скрипта у меня в браузере.

Я полез в код скрипта с тестами песочницы и увидел там следующий фрагмент в начале скрипта:

before(function() {
    this.clock = sinon.useFakeTimers();
});

after(function() {
    this.clock.restore();
});

То есть перед каждым тестом происходит подмена глобальных таймеров, а после каждого теста — обратная подмена (восстановление). Эта функциональность предоставляется mock-библиотекой Sinon.JS:

https://sinonjs.org/releases/latest/fake-timers/
https://ru.wikipedia.org/wiki/Mock-объект

В результате этих манипуляций при первом вызове функции в тестовом коде метод Date.now() возвращает не текущую метку времени (количество миллисекунд с 1 января 1970 года), как мы ожидаем, исходя из спецификации JavaScript, а число 0 (то есть таймер начинает отсчет миллисекунд от начала теста, а не с 1 января 1970 года). В результате условие Date.now() - call.time > t, прописанное в коде функции-декоратора, не выполняется для первого вызова функции из тестового кода и этот вызов не происходит. После чего тест оказывается не пройден.

В моем варианте решения первый вызов функции через декоратор обрабатывается отдельно, поэтому мой вариант решения работает. Я внес следующие исправления в код обсуждаемой функции-декоратора:

function debounce(f, ms) {
    let t = ms;
    function call () {
        if (Date.now() - call.time > t) {
            call.time = Date.now();
            f.apply(this, arguments);
        } else if (call.time === undefined) { // первый вызов обрабатываем отдельно
            call.time = Date.now();
            f.apply(this, arguments);
        }
    }
    // call.time = 0;
    return call;
}

Этот код проходит тесты из песочницы авторов учебника.
Tags: Образование, Программирование
Subscribe

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 0 comments