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