setInterval
/ clearInterval
по своей природе изменчив, но даже если это не ваш fancyCondition
, в любом случае, удаление этого ref
не будет вам дорого стоить.Я думаю, что даже с ref
он может улучшиться за счет инкапсуляции, и, в зависимости от вашего fancyCondition
, мы сможем получить такое же поведение чисто функциональным способом, используя setTimeout
вместо setInterval
/ * 1009.*.
Во-первых, давайте сделаем ваш пример конкретным, добавив счетчик, напечатав счетчик, а затем очистив интервал, когда мы достигнем значения 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
и clearInterval
и передачи функции clear
в наш обратный вызов мы передаем ей функцию continue
, которая вызывается, когда мы хотим продолжить, а не когда мы хотим выручить.Это позволяет нам передавать состояние вперед и изменять состояние без использования мутации и ref
s, используя вместо этого рекурсию.И это происходит с меньшим количеством кода.Я думаю, что это будет выглядеть довольно элегантно, если не совсем то, что вы просили:)