-
Notifications
You must be signed in to change notification settings - Fork 24.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Discussion: Timers] Background execution and ordering semantics #167
Comments
Honestly we haven't given timers a very thorough considerations, and background operation is completely unconsidered at this point - thanks for such a thorough writeup! I did add one simple feature recently - setTimeout(fn, 0) will now fire immediately (but in a different event loop, unlike setImmediate) rather than waiting for the next run loop frame as it currently does. This will be useful for time-slicing optimizations we have in mind. -Spencer
|
I haven't had a need for this yet but sense that a more comprehensive timer API is going to be necessary once apps act as background services that need to do scheduling. |
@jordwalke, I know you've been thinking about these problems, want to make sure you've seen this |
Fix typo in dependency React => react
@ide have you seen BTW: Our JS timers should be fired from a display link on the JS thread. The only reason why they're not, is because I didn't know you could run display links from a non-Main thread! It would clean up so much complexity if every frame, on the JS thread, it just called into JS and asked "so, do you have any timers ready"? Feel free to send a PR, I know that @tadeuzagallo has been working towards this but hasn't gotten to actually moving the timers to the JS thread. Having the timers be fired/managed from the JS thread will also make JS animations smoother because the pipeline is then more unidirectional and fewer things can delay/clog those timer messages being reported to the JS thread. Right now, if the main thread is blocked on updating UIViews, it can't send the timer message to the JS thread so that it can get work started. |
@jordwalke As for timers, our timers do run on the JS thread, for quite a while now, they are not implemented in JS though. TBC, everything runs based on a CADisplayLink on the JS thread, the only thing the displayLink on the main does right now is put VSync markers and count FPS. |
Any update on background timers? I'm working on a fleet tracking app, and if I can't track GPS or fetch data in the background I'm going to have to abandon react-native. |
@danielsandrewm no updates from my side. In the past when I've needed background tasks I wrote that code in Obj-C and then called into it from RN. |
@danielsandrewm , yes the runloop currently is suspended by the OS when the application is backgrounded. I wrote my background tasks in ObjC, like @ide did, which will fire an update event with state information when the application was brought o the foreground. The reality is that React Native doesn't entirely replace all classes of native applications. |
Moving this to ProductPains @ide - https://productpains.com/post/react-native/background-timer-execution/ I really want to see this but I think it'll have to be something that the community builds, in the short term at least. |
@brentvatne @ide Could either of you give code example or perhaps implement the fix for Pomodoro app to make it work 100% proper? |
@andrews is also thinking about timers and event ordering. We have some ideas floating around for hi-pri event precedence, and maybe even changing the JS runloop model to poll native rather than native always calling into it, which could give us more flexibility to do re-ordering and other queue processing in JS, and is probably more performant than calling in from native on every event like we are doing now.
|
Would it be possible to write a Swift module that runs whatever JavaScript it receives in a separate JavaScriptCore thread? Basically just a module that runs a separate thread in the background? |
Turns out React Native's timers (setTimeout / setInterval) don't run while the app is in the background: facebook/react-native#167 This patch replaces the global timer functions with those from the react-native-background-timer package, which work in the background. These timers won't magically make an application work in the background, but they will run if an application already happens to run in the background. That's our case while in a conference, so these timers will run, allowing XMPP pings to be sent and the conference to stay up as long as the user desires.
@ide So what is the ultimate way for iOS? I am really clueless, bcs iOS does not allow this timer to work when the app goes in background. I need to track GPS with some other important data every 5 minutes. As of now, I collect the data every 5 minutes in local async data and then push it to the server every 15 minutes. But on iOS, when the app goes background (Even screen is on or off), its no running this timer thread. Now, what could be the options here?
|
I wanted to start a discussion about the timers and an API that exposes the capabilities of the system timers. A couple of things I noticed were that the timers use CADisplayLink on the main thread and also listen to the UIApplication state notifications. To my knowledge this works well for timers that are coupled to the UI (requestAnimationFrame comes to mind, there may be other uses). I'd like to discuss timers that do other work e.g. interact with the event loop or schedule background tasks, and how they co-interact.
Event loop timers seem to fall into two categories - those that run at the end of the current loop and those that run right after. This is like process.nextTick and setImmediate. These aren't too hard to implement though there are a couple of gotchas like warning if a process.nextTick handler endlessly schedules itself before blowing the stack. I would also expect both of these timers to run even if the app is inactive or backgrounded (e.g. say you get a network response while the app is backgrounded and schedule some work to run soon after - it should run in the background).
Background task timers (setTimeout, setInterval) raise a couple of questions like the semantics of a zero-ms timeout. I would also propose that these run in the background as well to address the use of running code that does not need the app to be foregrounded. And, if the app is backgrounded there is little harm in running UI code anyway -- in cases where it does matter, the timer handler can check the UIApplication state.
Background task timers that repeat are also good candidates for energy optimization. One thing that OS X systems do is coalesce timers where possible, partly aided by the programmer providing a tolerance value. If many running apps schedule timers that can overlap within their respective tolerance windows, the system may wake the CPU just once to run all of those tasks together.
There is also a question of the ordering of all of these timers. My strawman proposal is:
Additionally, setTimeout(delay = 9, threshold = 2) should run before setTimeout(delay = 10, threshold = 0), and two setTimeouts with the same delay should run in the order that they were scheduled even if they have different thresholds. But to keep things simple at first I would recommend we go with whatever the system timer (NSTimer) does. There's an argument to be made that the timer handlers should use promises or semaphores, etc if they need to coordinate and not rely on the timer system.
The text was updated successfully, but these errors were encountered: