react-native: [BUG] setTimeout fires incorrectly when using chrome debug
Hey,
I’ve found an issue that means when you’re running on an iOS device and debugging in chrome setTimeouts can fire in the wrong order. If you place the following code block into a fresh app and run it you should get the error…
componentDidMount:function() {
this.runTest()
},
test:function(cb) {
var aFired = false
var bFired = false
var aExpected = new Date().getTime()+130
var bExpected = new Date().getTime()+500
console.log('Schedule A to fire @' + aExpected)
console.log('Schedule B to fire @' + bExpected)
var a = setTimeout(() => {
var now = new Date().getTime()
console.log('A fired', now, now - aExpected)
aFired = true
if (bFired) { console.log('ERROR: b fired early'); return;
}
if (aFired && bFired) { cb() }
}, 130)
var b = setTimeout(() => {
var now = new Date().getTime()
console.log('B fired', now, now - bExpected)
bFired = true
if (!aFired) { console.log('ERROR: A not fired'); return;
}
if (aFired && bFired) { cb() }
}, 500)
},
runTest:function() {
this.test(() => {
setTimeout(() => {
this.runTest()
}, 500)
})
},
On the console you’ll get something like…
index.ios.js:25 Schedule A to fire @1448977966848
index.ios.js:26 Schedule B to fire @1448977967218
index.ios.js:30 A fired 1448977966754 -94
index.ios.js:38 B fired 1448977966755 -463
index.ios.js:25 Schedule A to fire @1448977966902
index.ios.js:26 Schedule B to fire @1448977967272
index.ios.js:30 A fired 1448977966806 -96
index.ios.js:38 B fired 1448977966806 -466
index.ios.js:25 Schedule A to fire @1448977966951
index.ios.js:26 Schedule B to fire @1448977967321
index.ios.js:30 A fired 1448977966835 -116
index.ios.js:38 B fired 1448977966835 -486
index.ios.js:25 Schedule A to fire @1448977966983
index.ios.js:26 Schedule B to fire @1448977967353
index.ios.js:38 B fired 1448977966871 -482
index.ios.js:40 ERROR: A not fired
index.ios.js:30 A fired 1448977966871 -112
index.ios.js:32 ERROR: b fired early
… with the first 3 firing correctly and the last one incorrectly. I believe this bug is also what is causing issue #1693 as one of the setTimeout functions sometimes fires early in touchableHandleResponderGrant
About this issue
- Original URL
- State: closed
- Created 9 years ago
- Reactions: 9
- Comments: 47 (20 by maintainers)
RN 0.50 Same issue
I’ve experienced this while “Debug in Chrome” is active.
setTimeout
fires very early.Has it been confirmed that
TimerMixin
avoids this issue?I thought
TimerMixin
only helps with cleanup incomponentWillUnmount
?Still an issue in
0.46.3
, can we reopen this?For the people who are still facing this issue ! which is the case for us on RN 0.44.0 on Android :
We found indeed that the problem was related to the device time not being exactly as the time on the chrome remote debugger machine. This is due to the React Native javascript setTimeout function calling the native Timing.createTimer method with a Date.now() parameter which is not the same as the native side’s time when in remote js debug mode (where the javascript execution environment is the chrome browser or whatever debugger you’re using) as you can see in JSTimers.js#L93.
So, the solution for us was 1 of the following :
We chose the second solution as the first one wasn’t suited for our needs so we created a package called React Native Timer Native (I know, i’m not so good when it comes to naming things ^^') which is a fork of React Native Timer.
What it does --as a stupid hack-- is simply call a native method (when in dev mode) with the current javascript timestamp, then the native method gets its current timestamp using
System.currentTimeMillis()
, calculates the difference and returns it back to javascript, then the js package just adds that to whatever duration you passed to timer.setTimeout .The device time must be in the future compared to the remote debugging machine time otherwise it doesn’t work properly
It does get the timeouts executed with a decent precision (still need to substract the time that the bridge takes to call the native method so any inputs are welcome 😉 ).
I’m not certain that the issue that is reported on this thread is resolved. I’m getting the same behavior as described by many of you. One way to get things done and testable was to set the timers to a time less than 2 seconds, but when the app gets bigger it will be unbearable to test it.
RN0.42.0
So, I did a some small test on this,
This is super weird. The value of
performance.now()
is vastly different than when it’s assigned to a variable. SImilar results forDate.now()
.No wonder
setTimeout
executes instantly even if you give a timeout of 10 seconds.cc @vjeux @dmmiller @nicklockwood
For me, the setTimeout will wait until a re-render happens. So you have to touch the screen or scroll the page in order to fire the handler (assuming the time has passed). Disabling remote debugging makes it work as normal
Still an issue on 0.47.x as far as I can tell