【问题标题】:setInterval and setTimeout delay issuessetInterval 和 setTimeout 延迟问题
【发布时间】:2018-04-17 10:16:30
【问题描述】:

我一直在尝试通过 react-native 中的递归 setTimeout 来管理计时器。

但我面临的问题是,在某些设备中,计时器在处理过程中需要更多时间(例如 100-150 秒计时器中的 1-4 秒)。

我已经删除了 setInterval,因为它比递归 setTimeout 更糟糕。有什么想法可以让这个计时器变得完美吗?

编辑:主要问题是我在 2 个或更多设备上运行应用程序(在发布模式下)。计时器完美启动,但设备似乎有非常小的延迟,随着时间的推移而增加。

app中的api调用是并行完成的。

代码:

AnotherTimerHandler = () => {
    this.time = setTimeout(() => {
        if (this.state.gameState == timesup) {
            console.log(timesup)
            this.setState({ timer: this.state.timer - 1 });
            if (this.state.timer <= 0) {
                if (this.state.questionIndex < numberOfQuestions - 1) {
                    this.setState({ gameState: splash, timer: splashTime, QAndA: {}, correctAnswer: '', questionIndex: this.state.questionIndex + 1, answered: false })
                } else {
                    // console.log('123')
                    clearInterval(this.time)
                    console.log(this.state.playerMode)

                    if (this.state.playerMode) {
                        const { username, firstName, lastName } = this.props.navigation.state.params.userData;
                        firebase.database().ref(`tblGame/${gameIdToLoad}/gameWinners`).push({ Email: firebase.auth().currentUser.email, Name: firstName + ' ' + lastName })
                            .then(() => this.props.navigation.navigate('Winner', { gameId: gameIdToLoad, prizeAmount: this.props.navigation.state.params.QuizData.prizeAmount }))
                            .catch(err => alert(err))
                    } else { this.props.navigation.navigate('Winner', { gameId: gameIdToLoad, prizeAmount: this.props.navigation.state.params.QuizData.prizeAmount }); }
                }
            }
        }
        else if (this.state.gameState == playing) {
            console.log('playing')
            if (this.state.timer == questionTimer) {
                // console.log('playing1', this.state.timer)
                // this.setState({ answerLoaded: false })
                // this.QAndAHandler(Question)
                this.refs.circularProgress.performLinearAnimation(0, (questionTimer - 1) * 1000)
            }
            this.setState({ timer: this.state.timer - 1 })
            // if (this.state.timer == -1) {
            if (this.state.timer <= 0) {
                this.setState({ gameState: timesup, timer: answerTimer }); this.QAndAHandler(Ans);
                // console.log('playing2', this.state.timer)
            }
        }
        else if (this.state.gameState == splash) {
            console.log(splash)
            console.log(this.state.timer)
            this.setState({ timer: this.state.timer - 1 })
            if (this.state.timer == splashTime - 1) {
                this.QAndAHandler(Question)
            } else if (this.state.timer <= 0) this.setState({ timer: questionTimer, gameState: playing, answerLoaded: false })
        }
        // Dont call again if scren is being changed 
    return this.state.gameState == timesup && this.state.timer<=0 && !(this.state.questionIndex < numberOfQuestions - 1) ? null : this.AnotherTimerHandler()    
    }, 1000)
}

【问题讨论】:

  • 想到的是使用短间隔并检查Date.now()
  • 时间永远无法保证。你到底想达到什么目的?
  • 间隔之后你在叫什么?函数会不会太复杂以至于它们本身需要 1-4 秒才能执行,而不是 setTimeout / setInterval 不同步?
  • 我正在做 api 调用,但我很清楚这可能需要几秒钟,所以我正在并行进行数据库调用(在我的情况下为 firebase),所以即使在调用失败或需要时间之后,它也不是影响定时器。 @Panosh
  • 时机。是。绝不。保证。不在设备之间,不在一个设备内。您的代码需要期待这一事实并使用它。你的代码到底是什么,你想做什么?

标签: javascript react-native settimeout setinterval


【解决方案1】:

"时间无法保证",
但是 150ms 间隔 4 秒的差别确实可以看的很大。

避免这种情况的一种方法是将您的时间安排在更小的动态间隔中,自我纠正自己的延迟。

这是一个愚蠢的实现,它将下一个滴答声重新安排到下一秒,并在每个滴答声中自行纠正其延迟:

// Self-correcting setInterval
// intended for long intervals
// returns an object which "_id" property is the inner timeout id, so it can be canceled by clearInterval
function selfCorrectingInterval(cb, ms) {

  var innerTimeout = ms < 1000 ? 100 : 1000, // fire every ?s
    begin = performance.now(), // what time is it?
    last = begin + ms, // when should all this end?
    next = Math.min(innerTimeout, ms),
    prev = begin,
    result = {
      _id: setTimeout(inner, next)
    },
    passed = true; // a flag to avoid try-catch the callback
  return result;

  function inner() {
    if (!passed) return;
    passed = false; // set up the callback trap

    var shouldCall = false;
    var now = performance.now(),
      delay = (now - prev) - innerTimeout;
    prev += innerTimeout; // fixed increment

    if (last - now < 6) {
      shouldCall = true;
      begin = last; // start a new interval
      last += ms;
    }

    next = Math.min(innerTimeout - delay, last - now);
    result._id = setTimeout(inner, next);
    // call it at the end so we can cancel inside the callback
    if (shouldCall) {
      cb();
    }
    passed = true; // didn't throw we can continue
  }
}

// snippet-only tests
function test(ms) {

  function setTimeoutLoop(cb, ms) {
    function loop() {
      cb();
      setTimeout(loop, ms);
    }
    setTimeout(loop, ms);
  }

  var now = performance.now(),
    built_in_prev = now,
    timeout_prev = now,
    sCI_prev = now,
    built_in_elem = document.querySelector('#test_' + ms + ' .delay.built_in'),
    timeout_elem = document.querySelector('#test_' + ms + ' .delay.timeout'),
    sCI_elem = document.querySelector('#test_' + ms + ' .delay.sCI');

  setInterval(() =>  {
    var now = performance.now(),
      delay = (now - built_in_prev) - ms;
    built_in_prev += ms;
    built_in_elem.textContent = Math.round(delay);
  }, ms);

  setTimeoutLoop(() => {
    var now = performance.now(),
      delay = (now - timeout_prev) - ms;
    timeout_prev += ms;
    timeout_elem.textContent = Math.round(delay);
  }, ms);

  selfCorrectingInterval(() =>  {
    var now = performance.now(),
      delay = (now - sCI_prev) - ms;
    sCI_prev += ms;
    sCI_elem.textContent = Math.round(delay);
  }, ms);

}

test(1000);
test(5000);
test(60000);
test(150000);
[id^='test'] {
  border: 1px solid;
  padding: 0 12px
}
<div id="test_1000">
  <p>built in setInterval delay for 1000ms interval: <span class="delay built_in">0</span>ms</p>
  <p>built in setTimeout loop delay for 1000ms interval: <span class="delay timeout">0</span>ms</p>
  <p>selfCorrectingInterval delay for 1000ms interval: <span class="delay sCI">0</span>ms</p>
</div>
<div id="test_5000">
  <p>built in setInterval delay for 5000ms interval: <span class="delay built_in">0</span>ms</p>
  <p>built in setTimeout loop delay for 5000ms interval: <span class="delay timeout">0</span>ms</p>
  <p>selfCorrectingInterval delay for 5000ms interval: <span class="delay sCI">0</span>ms</p>
</div>
<div id="test_60000">
  <p>built in setInterval delay for 1 minute interval: <span class="delay built_in">0</span>ms</p>
  <p>built in setTimeout loop delay for 1 minute interval: <span class="delay timeout">0</span>ms</p>
  <p>selfCorrectingInterval delay for 1 minute interval: <span class="delay sCI">0</span>ms</p>
</div>
<div id="test_150000">
  <p>built in setInterval delay for 150s interval: <span class="delay built_in">0</span>ms</p>
  <p>built in setTimeout loop delay for 150s interval: <span class="delay timeout">0</span>ms</p>
  <p>selfCorrectingInterval delay for 150s interval: <span class="delay sCI">0</span>ms</p>
</div>

这就是我发现 Chrome 对setInterval 的实现已经自我纠正的方式......

【讨论】:

  • 嘿,谢谢你的回答,但在我的特殊情况下,我不能中继性能。现在因为要求它不应该中继设备时间,所以如果用户更改 devTime 这将不起作用.不过感谢您的回答:)
  • @PrashantDhameja 不确定您所说的 devTime 是什么意思,但是如果您谈论的是操作系统本地时间,那么 performance.now 与它无关,它基于 CPU 时钟。如果设备处于睡眠模式,它可能会卡住。
【解决方案2】:

如果您触发回调的时间间隔非常短,那么基于 javascript 的计时器将不适合,但如果您有更长的时间间隔来触发回调,那么这将起作用。 试试这个,https://github.com/ocetnik/react-native-background-timer 希望这会产生更好的结果,进动仍然是一个相对术语。

【讨论】:

  • 嘿,我已经检查过这个库,但似乎它不再被管理了。我尝试安装它,但在 iOS 中遇到问题。
猜你喜欢
  • 2020-09-10
  • 2012-06-16
  • 2012-10-30
  • 1970-01-01
  • 1970-01-01
  • 2013-10-28
  • 1970-01-01
  • 1970-01-01
  • 2017-06-28
相关资源
最近更新 更多