使用递归settimeout启动和停止计时器

发布于 2025-01-21 15:54:48 字数 711 浏览 0 评论 0原文

我正在尝试使用递归settimeout启动和停止计时器,因为set> setInterval不符合我的需求。当用户按下启动时,计时器开始计数。

1 ... 2 ... 3 ... 4..5 ... 1 ... 2 ... 3..4 ... 5 ... 1..2 ... 3..4。 ..5 ...

当用户按停止时,它应该停止此计时器循环。如果我再次按开始,更改按钮的状态(流变量),则计时器应再次执行相同的操作(1..2..3..4..5..1..1..2 ... 3。 .4 ... 5 ..)

我认为这样做的方法就是这样。


const ref = useRef(null);
useEffect(() => {
  if (!stream) {
    ref.current=setTimeout(repeatingFunc, 1000);

  } else {
    clearTimeout(ref.current);
  }
}, [stream]);

function repeatingFunc() {
  console.log("It's been 5 seconds. Execute the function again.");
  setTimeout(repeatingFunc, 5000);
}

我在做什么错?

计时器不会停止。即使我按停止,它仍在继续! (更改流状态值!)

I am trying to start and stop a timer with a recursive setTimeout, since setInterval doesn't fit my need. When the user presses start, the timer starts counting.

1...2...3...4..5...1...2...3..4...5...1..2...3..4...5...

And when the user presses stop, it should stop this timer loop. If I press start again, changing the state of the button (stream variable), the timer should do the same thing again (1..2..3..4..5..1..2...3..4...5..)

I thought the way to do it would be this.


const ref = useRef(null);
useEffect(() => {
  if (!stream) {
    ref.current=setTimeout(repeatingFunc, 1000);

  } else {
    clearTimeout(ref.current);
  }
}, [stream]);

function repeatingFunc() {
  console.log("It's been 5 seconds. Execute the function again.");
  setTimeout(repeatingFunc, 5000);
}

What am I doing wrong?

The timer doesn't stop. It keeps going even if I press stop! (changing the stream state value!)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

就是爱搞怪 2025-01-28 15:54:48

计时器不停止的原因是因为您仅存储初始settimeout调用的超时ID。当repotingFunc被称为另一个settimeout已注册,它具有新的超时ID。

因此,当您尝试使用clearTimeOut(ref.Current)清除超时时,您将传递过时的超时ID。

由于已经调用了先前的settimeout注册,因此您不再需要存储以前的ID,您可以简单地用新的ID替换。

const ref = useRef(null);

useEffect(() => {
  if (!stream) {
    ref.current = setTimeout(repeatingFunc, 1000);
  } else {
    clearTimeout(ref.current);
  }
}, [stream]);

function repeatingFunc() {
  console.log("It's been 5 seconds. Execute the function again.");
  ref.current = setTimeout(repeatingFunc, 5000);
  // ^^^^^^^^^^^^ update timeout ID after registering a new timeout
}

请注意,repotingFunc的范围是在最初调用settimeout(repotingFunc,1000)的时间点上固定的,这意味着状态和属性之类的范围可以包含过时的值。取决于您的上下文,这可能不会成为问题。


您可能还需要添加usefect清理功能,以清除组件下马的超时。否则,即使不再安装组件,您的循环也会继续下去。

useEffect(() => {
  if (!stream) {
    ref.current = setTimeout(repeatingFunc, 1000);
  }

  return () => clearTimeout(ref.current);
}, [stream]);

假设是一个布尔值(true/false),以上时,只要stream> stream的值已更改,或在组件下马上。

true更改为false时,将调用清理功能,并清除超时。

false更改为 true 也调用了清理功能,但由于当前的ref,因此导致了NO-OP。当前不再注册。然后设置新的超时。

将无效的ID传递到clearTimeout()无用的事情;没有例外。

请参阅: clearTimeOut()

The reason why the timer doesn't stop is because you only store the timeout ID of the initial setTimeout call. When repeatingFunc is called another setTimeout callback is registered, which has a new timeout ID.

So when you try to clear the timeout using clearTimeout(ref.current) you are passing an outdated timeout ID.

Since the previous setTimeout registration is already called, you no longer need to store the previous ID and you could simply replace it with the new one.

const ref = useRef(null);

useEffect(() => {
  if (!stream) {
    ref.current = setTimeout(repeatingFunc, 1000);
  } else {
    clearTimeout(ref.current);
  }
}, [stream]);

function repeatingFunc() {
  console.log("It's been 5 seconds. Execute the function again.");
  ref.current = setTimeout(repeatingFunc, 5000);
  // ^^^^^^^^^^^^ update timeout ID after registering a new timeout
}

Note that the scope of repeatingFunc is fixed in a point in time where you initially call setTimeout(repeatingFunc, 1000), meaning that things like states and properties can contain outdated values. This might or might not become a problem depending on your context.


You might also want to add a useEffect cleanup function that clears the timeout on component dismount. Otherwise your loop keeps going, even when the component is no longer mounted.

useEffect(() => {
  if (!stream) {
    ref.current = setTimeout(repeatingFunc, 1000);
  }

  return () => clearTimeout(ref.current);
}, [stream]);

Assuming stream is a boolean (true/false) the above will clear the current timeout whenever the value of stream is changed, or on component dismount.

When stream changes from true to false, the cleanup function will be called and the timeout will be cleared.

When stream changes from false to true the cleanup function is also called, but results in a no-op because the current ref.current is no longer registered. Then a new timeout is set.

Passing an invalid ID to clearTimeout() silently does nothing; no exception is thrown.

See: MDN - clearTimeout()

迷乱花海 2025-01-28 15:54:48

您可以定义这样的自定义计时器:

function CustomTimer(func) {
    this.id = -1;
    this.func = func;

    this.start = function (interval) {
        this.stop();

        if (this.func) {
            var t = this;
            this.id = setTimeout(function () { t.func.call(t) }, interval || 1000);
        }
    }

    this.stop =  function () {
        if (this.id >= 0) {
            clearTimeout(this.id);
            this.id = -1;
        }
    }
}

并使用它:

var myTimer = new CustomTimer(function () {
    console.log("It's been 5 seconds. Execute the function again.");
    this.start(5000);
});

myTimer.start(5000);

您可以在任何时候和间隔内更改要执行的函数。例如:在1秒内运行起始事件,然后每5秒5次做其他事情:

var secondFunc = function () {
    console.log("It's been 5 seconds. Execute the function again.");

    if (++this.count < 5)
        this.start(5000);
}

var myTimer = new CustomTimer(function () {
    console.log("First event at 1 second");

    this.count = 0;
    this.func = secondFunc;
    this.start(5000);
});

myTimer.start(1000);

You can define a custom timer like this:

function CustomTimer(func) {
    this.id = -1;
    this.func = func;

    this.start = function (interval) {
        this.stop();

        if (this.func) {
            var t = this;
            this.id = setTimeout(function () { t.func.call(t) }, interval || 1000);
        }
    }

    this.stop =  function () {
        if (this.id >= 0) {
            clearTimeout(this.id);
            this.id = -1;
        }
    }
}

And use it:

var myTimer = new CustomTimer(function () {
    console.log("It's been 5 seconds. Execute the function again.");
    this.start(5000);
});

myTimer.start(5000);

You can change the function to execute in any moment and also the interval. For example: run a start event in 1 second and then, 5 times every 5 seconds do other thing:

var secondFunc = function () {
    console.log("It's been 5 seconds. Execute the function again.");

    if (++this.count < 5)
        this.start(5000);
}

var myTimer = new CustomTimer(function () {
    console.log("First event at 1 second");

    this.count = 0;
    this.func = secondFunc;
    this.start(5000);
});

myTimer.start(1000);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文