提供取消函数而不是提供原始句柄,并向函数传递一个带有标志的对象,它可以检查它是否已被取消:
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(上图)和setInterval(setInterval 对间隔之间延迟的处理是...有趣的)之间存在轻微差异。防止函数在 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)
})();
您可以进一步概括这一点,但您了解基本概念。