我一直在尝试通过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)
}
答案 0 :(得分:0)
&#34; 永远不会保证时间&#34;,
但是150毫秒间隔的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);
&#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;
这就是我发现Chrome的setInterval
实施方式已经自行纠正...
答案 1 :(得分:0)
如果您的回调时间间隔非常短,那么基于javascript的计时器将不合适,但是如果您有更长的时间间隔来触发回调,那么这将有效。 试试这个,https://github.com/ocetnik/react-native-background-timer 希望这会给出更好的结果,因为它是相对的术语,所以进动仍然是有问题的。