在显示模块模式中取消失控 JavaScript timeouts/intervals

Cancelling runaway JavaScript timeouts/intervals within a revealing module pattern

我有一个相当标准化的揭示模块模式来创建用作实例的对象。有时,在这些模式中,如果模块不再被使用或在外部代码中引用,则需要取消超时或间隔。

此模式的简化示例:

function test() {
    window.timer = maketimer();
}

function maketimer() {
    var cls, my;

    my = {
        increment: 0,
        timerid: null,

        exec_timer: function() {
            my.timerid = window.setInterval(my.time, 2000);
        },

        time: function() {
            console.log("timer: ", my.timerid, my.increment++);
        }
    },

    cls = {
        //...
    }

    my.exec_timer();

    return cls;
};

test();

// some time later...
test();

test 被调用两次的情况下,无论出于何种原因,变量 window.timer 被替换为 maketimer 的第二个实例,但第一个实例计时器继续 运行.

很多时候,我的模块本质上链接到 DOM 个节点,而且 DOM 个节点经常与旧实例一起删除,所以理论上我可以检查节点不存在或其放置在 DOM 之外,然后在这种情况下取​​消间隔。

然而,这要通用得多,我希望能够在 DOM 环境之外处理超时。

试一试:在下面更新您的代码..

var settimer;
function test() {
   clearTimeout(settimer);
   settimer= setTimeout(function () { 
      window.timer = maketimer(); 
   }, 100);   
}

在这种情况下,我会将整个函数包装在一个包含 instance 变量的 IIFE 中。在其中,您保存计时器。而且每启动一个新的,旧的就被销毁:

(function(window) {
    var timerInstance = null;

    window.maketimer = function() {
        var cls, my;

        if(timerInstance) {
            timerInstance.destroyInstance();
        }

        my = {
            increment: 0,
            timerid: null,

            exec_timer: function() {
                my.timerid = window.setInterval(my.time, 2000);
            },

            time: function() {
                console.log("timer: ", my.timerid, my.increment++);
            },
            destroyInstance: function() {
                window.clearInterval(my.timerid);
            }
        },

        cls = {
            //...
        }

        my.exec_timer();

        timerInstance = my;

        return cls;
    }
})(window);

function test() {
    window.timer = maketimer();
}

test();
test();

出于好奇,为什么需要将实例放在全局变量上? window.timer 非常通用,可以被其他脚本覆盖。

上展开,我创建了以下示例,用于构建一个接受单个 DOM 节点的模块,如果 DOM节点已被使用:

<div id="element1">[code here that may be updated with XHR]</div>
<div id="element2">[code here that may be updated with XHR]</div>
(function(window) {
    var timerInstances = [];

    window.maketimer = function(element) {
        var cls, my, a;

        // find instances where the passed element matches
        for (a = 0; a < timerInstances.length; a += 1) {
            if (timerInstances[a].element === element) {
                console.log("instance already exists for element", element, "destroying...");
                timerInstances[a].in.destroyInstance();
            }
        }

        my = {
            increment: 0,
            timerid: null,

            exec_timer: function() {
                my.timerid = window.setInterval(my.time, 2000);
            },

            time: function() {
                console.log("timer: ", my.timerid, my.increment++);
            },

            destroyInstance: function() {
                window.clearInterval(my.timerid);
            }
        },

        cls = {
            //...
        }

        my.exec_timer();

        timerInstances.push({
            'element': element,
            'in': my
        });

        return cls;
    }
})(window);

function test(element) {
    window.timer = maketimer(element);
}

test(document.getElementById("element1")); // produces 1 timer
test(document.getElementById("element1")); // cancels last, produces 1 timer
test(document.getElementById("element2")); // produces 1 timer

这里的标识参数可以是任何东西——在本例中它是一个 DOM 节点,但它很容易是一个数字、字符串等。

接受的答案非常有帮助,如果您希望在文档中保持一次只有一个实例的规则,仍然是首选。