【问题标题】:Clear all kind of intervals清除所有类型的间隔
【发布时间】:2019-08-17 17:14:22
【问题描述】:

想象一个场景,你有一个 main 函数,它执行 2 个启动间隔的函数。这些函数作为 NodeJS 模块导入并执行。然后在一段时间后在主函数中清除Intervals。另外,请注意,我们将来在 main 函数中会有更多的间隔。

所以主要功能是

(() => {
    const intervals = [];
    intervals.push(require('./simpleInterval')());
    intervals.push(require('./asyncInterval')());

    setTimeout(() => {
        intervals.forEach(id => clearInterval(id));
    }, 1200)
})();

其中一种方法很简单

const intervalFoo = () => {
    return setInterval(() => {
        console.log('interval simple')
    }, 500);
};

module.exports = intervalFoo;

但是第二个包含一些异步代码,可以执行比间隔间隙更长的时间,但我不希望它在前一个“迭代”没有完成之前开始。这种情况下的解决方案是在开始时通过 id 清除间隔,然后在间隔的末尾(但在主体内)重新分配它。所以asyncInterval.js的代码是:

const sleep = require('./utilities/sleep');

const intervalFoo = () => {
    let intervalId;
    const checkE2Interval = async() => {
        clearInterval(intervalId);
        console.log('interval async');
        await sleep(120); //some long action
        return intervalId = setInterval(checkE2Interval, 100);
    };
    return intervalId = setInterval(checkE2Interval, 100); //returning id
};

module.exports = intervalFoo;

(睡眠只是一个承诺,在作为参数给出的超时时间后解决)

关于这个的问题是,我也在间隔内从asyncInterval.js 返回intervalId,我的问题是我不知道我应该如何清除这个东西。

【问题讨论】:

    标签: javascript promise setinterval intervals


    【解决方案1】:

    提供取消函数而不是提供原始句柄,并向函数传递一个带有标志的对象,它可以检查它是否已被取消:

    function mySetInterval(callback, ms, ...args) {
        let token = {
            cancelled: false
        };
        function wrapper(...args) {
            callback(token, ...args);
            if (!token.cancelled) {
                id = setTimeout(wrapper, ms, ...args);
            }
        }
        let id = setTimeout(wrapper, ms, ...args);
        return function cancel() {
            clearInterval(id);
            token.cancelled = true;
        }
    }
    

    由于0 是一个无效的计时器ID,我们可以安全地使用它作为间隔已被取消的标志。请注意,链式setTimeout(上图)和setIntervalsetInterval 对间隔之间延迟的处理是...有趣的)之间存在轻微差异。防止函数在 sleep 上暂停时被调用。为此,您必须有一个守卫并让函数特别支持异步函数:

    function mySetInterval(callback, ms, ...args) {
        let token = {
            cancelled: false
        };
        let running = false;
        async function wrapper(...args) {
            if (!running) {
                running = true;
                await callback(token, ...args);
                running = false;
            }
            if (!token.cancelled) {
                id = setTimeout(wrapper, ms, ...args);
            }
        }
        let id = setTimeout(wrapper, ms, ...args);
        return function cancel() {
            clearInterval(id);
            token.cancelled = true;
        }
    }
    

    使用该函数代替setInterval

    在你的异步函数中,如果它永远没有理由停止自己:

    const intervalFoo = () => {
        const checkE2Interval = async(token) => {
            console.log('interval async');
            await sleep(120); //some long action
            // If you had more logic here, you could short-circuit it by checking token.cancelled
        };
        return mySetInterval(checkE2Interval, 100); //returning id
    };
    

    如果它确实有理由自行停止,请保存cancel

    const intervalFoo = () => {
        let cancel = null;
        const checkE2Interval = async(token) => {
            console.log('interval async');
            await sleep(120); //some long action
            // If you had more logic here, you could short-circuit it by checking token.cancelled
            // If you wanted not to continue the timer, you'd call cancel here
        };
        return cancel = mySetInterval(checkE2Interval, 100); //returning id
    };
    

    那么,你需要取消的地方:

    (() => {
        const cancellers = [];
        cancellers.push(require('./simpleInterval')());
        cancellers.push(require('./asyncInterval')());
    
        setTimeout(() => {
            cancellers.forEach(cancel => cancel());
        }, 1200)
    })();
    

    现场示例:

    const sleep = ms => new Promise(resolve => {
        setTimeout(resolve, ms);
    });
    
    function mySetInterval(callback, ms, ...args) {
        let token = {
            cancelled: false
        };
        function wrapper(...args) {
            callback(token, ...args);
            if (!token.cancelled) {
                id = setTimeout(wrapper, ms, ...args);
            }
        }
        let id = setTimeout(wrapper, ms, ...args);
        return function cancel() {
            clearInterval(id);
            token.cancelled = true;
        }
    }
    
    const intervalFoo = () => {
        let cancel = null;
        const checkE2Interval = async(token) => {
            console.log('interval async');
            await sleep(120); //some long action
            // If you had more logic here, you could short-circuit it by checking token.cancelled
            // If you wanted not to continue the timer, you'd call cancel here
        };
        return cancel = mySetInterval(checkE2Interval, 100); //returning id
    };
    
    (() => {
        const cancellers = [];
        cancellers.push(intervalFoo());
    
        setTimeout(() => {
            console.log("Cancelling");
            cancellers.forEach(cancel => {
                cancel();
            });
        }, 1200)
    })();

    带有running 标志的实时示例:

    const sleep = ms => new Promise(resolve => {
        setTimeout(resolve, ms);
    });
    
    function mySetInterval(callback, ms, ...args) {
        let token = {
            cancelled: false
        };
        let running = false;
        async function wrapper(...args) {
            if (!running) {
                running = true;
                await callback(token, ...args);
                running = false;
            }
            if (!token.cancelled) {
                id = setTimeout(wrapper, ms, ...args);
            }
        }
        let id = setTimeout(wrapper, ms, ...args);
        return function cancel() {
            clearInterval(id);
            token.cancelled = true;
        }
    }
    
    const intervalFoo = () => {
        let cancel = null;
        const checkE2Interval = async(token) => {
            console.log('interval async');
            await sleep(120); //some long action
            console.log('awake');
            // If you had more logic here, you could short-circuit it by checking token.cancelled
            // If you wanted not to continue the timer, you'd call cancel here
        };
        return cancel = mySetInterval(checkE2Interval, 100); //returning id
    };
    
    (() => {
        const cancellers = [];
        cancellers.push(intervalFoo());
    
        setTimeout(() => {
            console.log("Cancelling");
            cancellers.forEach(cancel => {
                cancel();
            });
        }, 1200)
    })();

    您可以进一步概括这一点,但您了解基本概念。

    【讨论】:

    • 无法复制。我已将我的main.jsasyncInterval.js 更改为您的代码gist.github.com/d0peCode/cc8d306a7b9561a57f6d1696f2a111aa,我收到来自if(cancel) cancel(); 的错误TypeError: cancel is not a function
    • 什么意思?我在我的 IDE 中运行 main.js。同样在 mySetInterval 函数中,您将间隔 id 分配给 id 变量,然后您清理 token.id 我不明白
    • 我在 codesanbox 上的 node.js 操场上创建了演示:codesandbox.io/s/ecstatic-merkle-krimg
    • 所以我已经修正了错字,但现在它不是每 120 毫秒安慰一次字符串,因为似乎 cancel 总是正确的(函数)
    • @dopeCode - 正如我所说(在我刚刚删除的评论中,更新了答案),还有另一个问题。我现在已修复它,请参阅修订后的答案,其中包含实时副本。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多