setInterval和setTimeout延迟问题

时间:2018-04-17 10:16:30

标签: javascript react-native settimeout setinterval

我一直在尝试通过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)
}

2 个答案:

答案 0 :(得分:0)

&#34; 永远不会保证时间&#34;,
但是150毫秒间隔的4秒差异确实可以说是很大。

避免这种情况的一种方法是将你的时间分成较小的动态间隔,自我纠正自己的延迟。

这是一个愚蠢的实现,将重新安排下一个滴答到下一秒,在每个滴答处纠正自己的延迟:

&#13;
&#13;
// 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);
&#13;
[id^='test'] {
  border: 1px solid;
  padding: 0 12px
}
&#13;
<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>
&#13;
&#13;
&#13;

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

答案 1 :(得分:0)

如果您的回调时间间隔非常短,那么基于javascript的计时器将不合适,但是如果您有更长的时间间隔来触发回调,那么这将有效。 试试这个,https://github.com/ocetnik/react-native-background-timer 希望这会给出更好的结果,因为它是相对的术语,所以进动仍然是有问题的。

相关问题