清除各种间隔

Clear all kind of intervals

想象这样一种场景,您的主要功能执行 2 个开始间隔的功能。这些函数作为 NodeJS 模块导入并执行。然后在一段时间后在主函数中清除间隔。另请注意,我们将在未来的主要功能中有更多的间隔。

所以主要功能是

(() => {
    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;

但是第二个包含一些异步代码,可以执行比间隔间隙更长的时间,但我不希望它在前一个 "iteration" 未完成之前开始。这种情况的解决方案是在开始时通过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,我的问题是我不知道我应该如何清除这个东西。

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

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 对间隔之间延迟的处理是.. .interesting.)另请注意,上面没有任何内容可以阻止函数在 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)
})();


您可以进一步概括这一点,但您已经掌握了基本思想。