如何使用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 && 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()时,递归函数仍然继续运行(内存泄漏)。 我很想学习如何使用当前结构停止递归函数并防止内存泄漏。请赐教!

解决方法

首先,它并不是真正的递归,因为它在事件循环中将回调排队,而不是直接调用回调。因此,您不必担心调用堆栈上的内存不足。

要停止调用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中。