setInterval
/clearInterval
本质上是可变的,但即使它不是你fancyCondition
的,所以在ref
这里删除它不会给你带来很多好处。我认为即使ref
它可以通过封装进行改进,并且稍微取决于您fancyCondition
,我们应该能够通过使用setTimeout
而不是setInterval
/以纯粹的功能方式获得相同的行为clearInterval
。
首先,让我们通过添加一个计数器、打印计数、然后在达到计数 5 时清除间隔来使您的示例具体化,这样我们就可以使用一些东西了:
let intervalIdRef = ref(None);
let count = ref(0);
let clearInterval = () =>
switch (intervalIdRef^) {
| Some(intervalId) => Js.Global.clearInterval(intervalId)
| None => ()
};
let intervalId = Js.Global.setInterval(() => {
if (count^ < 5) {
Js.log2("tick", count^);
count := count^ + 1;
} else {
Js.log("abort!");
clearInterval();
}
}, 200);
intervalIdRef := Some(intervalId);
我认为我们应该做的第一件事是通过将定时器状态/句柄包装在一个函数中并传递clearInterval
给回调来封装它,而不是将它作为一个单独的函数,我们可能会在不知道它是否真的做任何事情的情况下多次调用它:
let setInterval = (timeout, action) => {
let intervalIdRef = ref(None);
let clear = () =>
switch (intervalIdRef^) {
| Some(intervalId) => Js.Global.clearInterval(intervalId)
| None => ()
};
let intervalId = Js.Global.setInterval(() => action(~clear), timeout);
intervalIdRef := Some(intervalId);
};
let count = ref(0);
setInterval(200, (~clear) => {
if (count^ < 5) {
Js.log2("tick", count^);
count := count^ + 1;
} else {
Js.log("abort!");
clear();
}
});
我们现在已经摆脱了全局计时器句柄,我认为它回答了您最初的问题,但我们仍然坚持count
作为全局状态。所以让我们也摆脱它:
let rec setTimeout = (timeout, action, state) => {
let continue = setTimeout(timeout, action);
let _:Js.Global.timeoutId =
Js.Global.setTimeout(() => action(~continue, state), timeout)
};
setTimeout(200, (~continue, count) => {
if (count < 5) {
Js.log2("tick", count);
continue(count + 1);
} else {
Js.log("abort!");
}
}, 0);
在这里,我们将问题颠倒了一点。我们没有使用setInterval
andclearInterval
并将clear
函数传递到我们的回调中,而是传递给它一个continue
在我们想要继续而不是在我们想要退出时调用的函数。这允许我们向前传递状态,并ref
通过使用递归而不使用突变和 s 来更改状态。而且它用更少的代码来做到这一点。我认为这很优雅,如果不是你所要求的:)