区间清理原因

问题描述 投票:1回答:1

在Reason中,当满足某些条件时,有一个最优雅的方法可以让自己清除?在JavaScript中,我可以这样做:

var myInterval = setInterval(function () {
    // do some stuff
    if (fancyCondition) {
        clearInterval(myInterval);
    }
}, 1000);

在理性中,到目前为止我提出的最好的是:

let intervalIdRef = ref(None);
let clearInterval = () =>
    switch (intervalIdRef^) {
    | Some(intervalId) => Js.Global.clearInterval(intervalId)
    | None => ()
    };
let intervalId = Js.Global.setInterval(() => {
    /* do some stuff */
    fancyCondition ? clearInterval() : ();
}, 1000);
intervalIdRef := Some(intervalId);

有没有办法避免使用ref

functional-programming reason bucklescript
1个回答
3
投票

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);

在这里,我们将问题颠倒过来了。我们没有使用setIntervalclearInterval并将clear函数传递给我们的回调,而是传递了一个continue函数,当我们想要继续而不是当我们想要拯救时,它被调用。这允许我们向前传递状态,并通过使用递归来改变状态而不使用变异和refs。它使用更少的代码来实现这一点。我认为这有点非常优雅,如果不是你要求的:)

© www.soinside.com 2019 - 2024. All rights reserved.