Как остановить рекурсивный цикл с помощью `requestAnimationFrame`

#javascript #typescript #recursion #memory-leaks #requestanimationframe

#javascript #typescript #рекурсия #утечки памяти #requestanimationframe

Вопрос:

У меня есть класс с функцией рекурсивного цикла, подобной приведенной ниже:

 class Foo {
    private isLoopOnFlg: boolean = false;

    public StartLoop() {
        this.isLoopOnFlg = true;

        this.recursiveLoopWithDelay(() => {
            // Modifying DOM objects
        }, 1000);
    }

    public StopLoop() {
        this.isLoopOnFlg = false;
    }

    // THE PROBLEM
    private recursiveLoopWithDelay(loopFn: any, delay: number) {
        let stamp = Date.now();

        // How do I stop the loop using `isLoopOnFlg`?
        function _loop() {
            if (Date.now() - stamp >= delay) {
                loopFn();
                stamp = Date.now();
            }

            window.requestAnimationFrame(_loop);
        }

        window.requestAnimationFrame(_loop);
    }
}

  

Как вы видите, функция recursiveLoopWithDelay не будет остановлена — поэтому я добавил новую закрытую переменную isLoopOnFlg и хочу использовать ее для остановки рекурсивной функции — но я не уверен, как это сделать. Я попытался остановить функцию, добавив новый параметр keepLoopFlg в функцию, как показано ниже:

 ...
private recursiveLoopWithDelay(loopFn: any, delay: number) {
    let stamp = Date.now();

    // Added a new param `keepLoopFlg` but it's not changed when `StopLoop()` is called
    function _loop(keepLoopFlg: boolean) {
        if (keepLoopFlg amp;amp; Date.now() - stamp >= delay) {
            loopFn();
            stamp = Date.now();
        }

        window.requestAnimationFrame(_loop.bind(this, this.isLoopOnFlg)); // Used `.bind(...)`
    }

    window.requestAnimationFrame(_loop.bind(this, this.isLoopOnFlg));
}
...
  

Но приведенный выше код не будет делать то, что я хочу — при StopLoop() вызове рекурсивная функция все еще продолжает работать (утечка памяти).
Я хотел бы узнать, как остановить рекурсивную функцию с текущей структурой и предотвратить утечку памяти. Пожалуйста, просветите меня!

Ответ №1:

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

Чтобы прекратить вызов requestAnimationFrame, вы просто не вызываете его. Вопрос в том, когда вы хотите прекратить его вызывать? Если вы предоставляете служебную функцию для кого-то другого, вы обычно позволяете им указывать, когда «отписаться» или остановить обновления.

 private recursiveLoopWithDelay(loopFn: any, delay: number) {
    const self = this;
    let stamp = Date.now();

    function _loop() {
        // If we aren't looping anymore, just exit the code.
        // Don't requeue requestAnimationFrame
        if (!self.isLoopOn) {
            return;
        }

        if (Date.now() - stamp >= delay) {
            loopFn();
            stamp = Date.now();
        }

        window.requestAnimationFrame(_loop);
    }

    window.requestAnimationFrame(_loop);
}
  

Вы также можете пропустить привязку, используя лексическую область, как я сделал здесь . Сохранение this в переменной self , которую я могу искать в любой момент.

Комментарии:

1. Ах, готя, ключи self = this и позиция isLoopOn . Спасибо, что помогли мне!