React-native: Add support for AlarmManager in Timing to allow proper handling of long timers

Created on 16 Mar 2017  ·  173Comments  ·  Source: facebook/react-native

Setting timers for multiple minutes isn't handled properly in React Native on Android: it keeps the Timing module awake instead of relying on the system waking us up when the timer should go off.

We should explore setting a cut off at which we delegate to AlarmManager and Handler.postDelayed instead of handling the timers using framecallbacks.

Bug JavaScript Android

Most helpful comment

@skv-headless Opinion incoming. :)

In my opinion that attitude towards problems needs to go away. I've been seeing it pop up more often and it's causing people to ignore warnings. They're warnings. They warn you. Don't ignore them, do something. I've seen apps where all warnings just get ignored, even deprecation notices. Then your app breaks and you're left wondering why.

In this case, you can configure these timeouts and lower them, or setup a different approach of dealing with them. Maybe even poke the maintainers of the libraries and ask them to help come up with a solution.

My advise, is to follow this thread until someone smart comes up with an actual solution, so you can then educate yourself with that answer. And then maybe, in the meantime (if you can't / don't want to teckle the issue) ignore the warnings.

TL;DR;

Yes, you can ignore the warnings temporarily. Just check in every now and then to see what the status is and if there's any action required.

All 173 comments

Great improvement!

this would help with socket.io which keeps a timer of 85000ms by default. On RN master the threshold is 60000ms.

And Firebase which is also using long timers.

I am getting following warning while using firebase library

Setting a timer for a long period of time, i.e. multiple minutes, is a performance and correctness issue on Android as it keeps the timer module awake, and timers can only be called when the app is in the foreground. See https://github.com/facebook/react-native/issues/12981 for more info. (Saw setTimeout with duration 111862ms)

How to get rid of this warning...

I am getting following warning while using firebase library too. Somebody know how to solve this problem?

I am also getting this warning with firebase.

"firebase": "^3.9.0",
"react-native": "0.44.0"

Same issue (85000ms) but without firebase. My list of packages:

  "dependencies": {
    "apisauce": "0.11.0",
    "format-json": "1.0.3",
    "lodash": "4.17.4",
    "markdown-it": "^8.3.1",
    "native-base": "^2.1.3",
    "normalizr": "^3.2.2",
    "prop-types": "^15.5.10",
    "querystringify": "1.0.0",
    "ramda": "0.23.0",
    "react": "16.0.0-alpha.6",
    "react-markdown": "^2.5.0",
    "react-native": "0.44.0",
    "react-native-animatable": "1.2.0",
    "react-native-config": "0.4.2",
    "react-native-device-info": "0.10.2",
    "react-native-drawer": "2.3.0",
    "react-native-htmlview": "0.9.0",
    "react-native-i18n": "1.0.0",
    "react-native-linear-gradient": "^2.0.0",
    "react-native-photo-view": "^1.2.0",
    "react-native-router-flux": "3.39.1",
    "react-native-scrollable-tab-view": "*",
    "react-native-share": "^1.0.20",
    "react-native-vector-icons": "4.1.1",
    "react-navigation": "^1.0.0-beta.9",
    "react-redux": "5.0.4",
    "redux": "3.6.0",
    "redux-persist": "4.6.0",
    "redux-saga": "0.15.3",
    "reduxsauce": "0.4.1",
    "seamless-immutable": "7.1.2"
  },
  "devDependencies": {
    "ava": "^0.18.2",
    "babel-eslint": "^7.1.1",
    "babel-preset-es2015": "^6.18.0",
    "enzyme": "^2.6.0",
    "husky": "^0.13.1",
    "ignite-animatable": "^0.3.1",
    "ignite-dev-screens": "^2.0.0-beta.9",
    "ignite-i18n": "^0.1.1",
    "ignite-ir-boilerplate-2016": "^0.2.2",
    "ignite-vector-icons": "^0.2.1",
    "mockery": "^2.0.0",
    "nyc": "^10.1.2",
    "react-addons-test-utils": "^15.3.1",
    "react-dom": "^15.4.0",
    "react-native-mock": "^0.3.1",
    "reactotron-apisauce": "^1.7.0",
    "reactotron-react-native": "^1.7.0",
    "reactotron-redux": "^1.7.0",
    "reactotron-redux-saga": "^1.7.0",
    "snazzy": "^6.0.0",
    "standard": "^8.6.0"
  }

I'm experiencing a similar issue using 0.44.0 on Android. I am also not using firebase:

Setting a timer for a long period of time, i.e. multiple minutes, is a performance and correctness issue on Android as it keeps the timer module awake, and timers can only be called when the app is in the foreground. See https://github.com/facebook/react-native/issues/12981 for more info. (Saw setTimeout with duration 85000ms)

Hi there guys.. was wondering Is there any quick work around for this. I'm using:

react-native 0.44 react 16.0.0-alpha.6 feathers-socketio 1.6.0

Its very annoying popping out while I'm developing.. is there anyway I can hide the warning for now??

Same issue here with firebase 3.9.0

If it bothers you just add console.ignoredYellowBox = ['Setting a timer'];

@skv-headless Opinion incoming. :)

In my opinion that attitude towards problems needs to go away. I've been seeing it pop up more often and it's causing people to ignore warnings. They're warnings. They warn you. Don't ignore them, do something. I've seen apps where all warnings just get ignored, even deprecation notices. Then your app breaks and you're left wondering why.

In this case, you can configure these timeouts and lower them, or setup a different approach of dealing with them. Maybe even poke the maintainers of the libraries and ask them to help come up with a solution.

My advise, is to follow this thread until someone smart comes up with an actual solution, so you can then educate yourself with that answer. And then maybe, in the meantime (if you can't / don't want to teckle the issue) ignore the warnings.

TL;DR;

Yes, you can ignore the warnings temporarily. Just check in every now and then to see what the status is and if there's any action required.

@imamatory I think is by Rectotron

I think is by Rectotron

If so, this warning can be simply ignored.
...however my android emulator getting slow sometimes, maybe it is one of reasons.

@imamatory I hope the real solution is just as easy

Hi, I think I found the solution:
Firstly you have to find the following file in your project: libraries/Core/Timers/JSTimer;js
Open it and you just have to change this const MAX_TIMER_DURATION_MS, to increase above your duration, wrote in the end of warning !

@nicolasZZ hi, Thanks for the solution. but Is that a good practice to modify the JSTimer library rather than our own code?

@nicolasZZ @AmroAly In my mind this solution is not a good practice because it is very unstable, the best is ignore the alert while the official solution is released.

console.ignoredYellowBox = [
    'Setting a timer'
]

the @skv-headless's solution is good but in my case only works with "enter" between of the parenthesis

@rigobcastro Thanks that hides the annoying alerts.

@DZuz14 it would be awesome if you could sent a PR or atleast get the conversation started. Thanks 👍

hi~
I used socket.io. nodejs and RN44 or RN 45
I saw warning this timers.

I found this solution
reference reactotron PR

personal server nodejs and scoket.io

const express = require('express')
const app = express()
const server = require('http').Server(app)
const io = require('socket.io')(server, {pingTimeout: 30000})

Thanks!

I have a simple react native project for android and I'm using firebase auth with react native for Login and Signup, but I got that yellow error, What should I do? Link of my Question in stackoverslow (https://stackoverflow.com/questions/44603362/setting-a-timer-for-a-long-period-of-time-i-e-multiple-minutes )

Thank You @DZuz14
I Got This Error When I run react-native run-android

D:\Projects 2016\Web\Android\mohajerkade-android\android\app\src\main\java\com\mohajerkade\AlarmManagerModule.java:40: error: cannot find symbol
       Intent intent = new Intent(context, AlarmReceiver.class);
                                           ^
  symbol:   class AlarmReceiver
  location: class AlarmManagerModule
1 error
:app:compileDebugJavaWithJavac FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:compileDebugJavaWithJavac'.
> Compilation failed; see the compiler error output for details.

I want to use firebase auth with react native for Login and Signup but I got That yellow error,
1: What is This Module Exactly?
2: Why My Project need This Module?

I asked stackoverflow through this link: (https://stackoverflow.com/questions/44603362/setting-a-timer-for-a-long-period-of-time-i-e-multiple-minutes)
I Reported to Google Firebase Team through this link: (https://github.com/firebase/firebase-js-sdk/issues/97)

@DZuz14 I got the same question as @saeedhei .

Basically we are using the firebase node module that uses these long period timers.

Adding your module is not enough I guess, we actually have to rewrite the code within the firebase node module to use your Alarms rather than Javascript timers, right?

Thanks @DZuz14

So if you look into the npm package called firebase you will find that it uses setTimeout in quite a lot of places. This is OK since the library was written for websites and node.js servers. react-native is kind of a node.js server but it runs on a phone - and as the warning correctly points out - long running Javascript timers are not good in this environment and might drain the battery and have other unwanted side effects.

So I guess we have to convince the Firebase guys actually to change to your approach and distribute a package made especially for react-native.

@BerndWessels https://www.npmjs.com/package/react-native-firebase Have you looked at this? Says it allows firebase to run on the native thread. Not sure how true that is, but might be worth a look. You might even be using that, I'm not sure.

@DZuz14 Thanks, I'll check that out.

console.ignoredYellowBox = [
'Setting a timer'
]

@rigobcastro Where can I input or paste this code?

@Jazz747 On the top of your setup file for example app.js, index.android.js or index.ios.js

console.ignoredYellowBox = [ 'Setting a timer' ]
This code only hide the warning inside react native.

How about the warning inside browser console?

Does anyone know if someone from the RN team or the community is working on a solution to this issue?

The warning popup redirects to this issue but I can't find any proper solution here other than to hide the warning, which isn't a solution at all. Any ideas or suggestions?

@SuhairZain Everything seems to indicate that are errors of external libraries to RN

@rigobcastro This warning can be easily reproduced with a setInterval() call with a time long enough (which was 5 minutes in my case). So I'd say this isn't something caused by 3rd party libs.

BTW, I've since found a library here: react-native-background-timer which helps me get the job done.

@SuhairZain yes u right! my problem was with 3rd party lib but the owner fixed the error (I imagine they just changed some setInterval()) and the warning disappeared.

Good recommendation, thanks.

i have same problem here. And still couldent understand how should i handle with this problem.

rn: 0.45.1
firebase: 4.1.3

Dear @escalepion I reported to firebase team: (https://github.com/facebook/react-native/issues/12981#issuecomment-309595327)

@escalepion If your problem is about setting long running timers, please take a look at my comment above and see if it helps you.

@SuhairZain sorry i couldent understand that how can use setInterval or react-native-background-timer :( where and how will i write the code ? do u have an example?

Does anyone have a tip on how to figure out which library is causing the harm? I recently upgraded RN to 0.46 and suddenly this error appeared. I have quite some libraries in my project and I can only think of investigating them one by one, which obviously isn't ideal.

Cheers!

@abeltje1, if you figure out how fix this issue, could you, please, update here

@liketurbo for sure I will

I'm having this issue alongside with redux-saga to revalidate the API token every 30 minutes.

Hence, in this case, awaiting for this long is actually correct. And I guess that there are others scenarios which long waiting are expected.

Then my question is, why is this a warning? What can break/happen? Am I not supposed to being able to use any LLT (long lived transactions)?

If LLT are ok. What would be the best approach to handle this issue? Expanding AlarmManager time?

Thanks in advance ;)

I'm having the same issue as @sospedra. I'm using redux-saga and I have a saga that refreshes some data in the app in the background every 3 minutes, so the saga includes the code yield call(delay, 3 * 60000);. As a result I'm getting this "Setting a timer for a long period of time..." error.

I'm also wondering what the suggested approach is for something like this. What problems will I run into if I just leave my code working this way. The error message implies there could be performance issues, and perhaps that the timer won't actually get fired at the correct time if the app is in the background?

If there are likely to be problems with this code setting a 3 minute timer, what alternative solution would achieve the same functionality and not run into these problems?

still getting same issue.

I think, the only solution it's move to Native Android Development

The issue still cannot solve? Anyone found the solution?

I am getting warning while using firebase.

If the issue isn't from a 3rd party library, but from you setting a long timeout yourself, a good solution is to use the react-native-background-timer library. It's README contains a simple example as well.

Unfortunately, if the issue is from a 3rd party library, you probably have no solution other than to find the place where the setTimeout is called and raise an issue / submit a PR to use the aforementioned library instead, please try my possible solution below at https://github.com/facebook/react-native/issues/12981#issuecomment-331074444 and try if it works for you.

Please make great timeout on android too.

react-native-background-timer is a good workaround as @SuhairZain said. this can clear warning msg

I'm getting this warning when using socket.io

because socket.io has a timer of 85ms

FWIW this only happens to me when I use Reactotron while developing...

Drove me crazy for a while I have to admit but I knew I could not ignore the message

I'm no longer working on the project that I had this issue with, so I can't really test if this approach works, but it should, at least in theory. If the error is from a 3rd party module using setTimeout or setInterval with a long delay, this should be able to fix your issue. What it does is monkey patch the global setTimeout and setInterval to use react-native-background-timer's instead:

import BackgroundTimer from 'react-native-background-timer';

setTimeout = BackgroundTimer.setTimeout;
setInterval = BackgroundTimer.setInterval;
clearTimeout = BackgroundTimer.clearTimeout;
clearInterval = BackgroundTimer.clearInterval;

Write this code somewhere in your early initialization, preferably index.android.js or index.ios.js at the earliest place you can. If someone can test this out, it'd be highly appreciated.

@SuhairZain: your suggestion is working for me so far, with a few tweaks:

setTimeout = BackgroundTimer.setTimeout.bind(BackgroundTimer)
setInterval = BackgroundTimer.setInterval.bind(BackgroundTimer)
clearTimeout = BackgroundTimer.clearTimeout.bind(BackgroundTimer)
clearInterval = BackgroundTimer.clearInterval.bind(BackgroundTimer)

@levity I added your code to my app.js , but I got this

undefined is not an object (evaluating 'RNBackgroundTimer.setTimeout')

@realtebo sounds like you may not have completed the installation of react-native-background-timer as described in their README by either running react-native link or using CocoaPods.

@levity : do you mean running react-native link ?

npm install -g react-native-cli
react-native link
Scanning folders for symlinks in C:\Users\realtebo\Downloads\manager2\node_modules (54ms)
react-native link can not be used in Create React Native App projects. If you need to include a library that relies on custom native code, you might have to eject first. See https://github.com/react-community/create-react-native-app/blob/master/EJECTING.md for more information.

I dont't want to eject .... so,.. what can I do?

Oh, sorry, I don't know how to help you with a Create React Native App project. I've never used that myself.

Hi there,
I got some issues with react-native: ^0.49.3...

cf. https://github.com/ocetnik/react-native-background-timer/issues/65

Hope you can help, thanks for your time ;)

Hi, I have the same issue I am using 0.49 , it appeared after doing this

export function saveUserData(userInfo){
  return (dispatch) => {
    dispatch(saveDataRequest())
    const userId = firebase.auth().currentUser.uid;
    return firebase.database().ref('users/' + userId).set(userInfo)
      .then(() => {
        dispatch(saveDataSuccess());
        dispatch(NavigationActions.navigate({ routeName: 'TabContactNavigation' }))
      })
      .catch((error) => {
        dispatch(saveDataFailure(error))
      });
  }
}

The above is Action in redux , which also uses redux-thunk.. When a button is clicked to save data from a form to firebase when save successful it dispatches saveDataSuccess() and then navigate to the next tab.
Now I am suspecting the problem to appear to be here const userId = firebase.auth().currentUser.uid but I am not sure. Here is a screen shot of the problem. (the app acts fine but this warning appears, I dont know if i should simply ignore it) or maybe I am doing something wrong here, need your help guys, thanx

warning

warning2

hey, the warning is about setTimeout are you using it in your component?
and yes, the line const userId = firebase.auth().currentUser.uid; is not working, that is a promise and when you use userId it still not comming.
a better way to do it is to get the userId from the actionCreator, you already are sending the userInfo variable, try this:

i will assume than the userInfo is coming from firebase auth login, so you have the uid in it

export function saveUserData(userInfo){
return (dispatch) => {
dispatch(saveDataRequest())
//const userId = firebase.auth().currentUser.uid;
return firebase.database().ref('users/' + userInfo.uid).set(userInfo)
.then(() => {
dispatch(saveDataSuccess());
dispatch(NavigationActions.navigate({ routeName: 'TabContactNavigation' }))
})
.catch((error) => {
dispatch(saveDataFailure(error))
});
}
}
i hope this could help, and sorry for my bad english =P

BlisS

El 23/10/2017, a las 23:59, Yasir notifications@github.com escribió:

Hi, I have the same issue I am using 0.49 , it appeared after doing this

export function saveUserData(userInfo){
return (dispatch) => {
dispatch(saveDataRequest())
const userId = firebase.auth().currentUser.uid;
return firebase.database().ref('users/' + userId).set(userInfo)
.then(() => {
dispatch(saveDataSuccess());
dispatch(NavigationActions.navigate({ routeName: 'TabContactNavigation' }))
})
.catch((error) => {
dispatch(saveDataFailure(error))
});
}
}
The above is Action in redux , which also uses redux-thunk.. When a button is clicked to save data from a form to firebase when save successful it dispatches saveDataSuccess() and then navigate to the next tab.
Now I am suspecting the problem to appear to be here const userId = firebase.auth().currentUser.uid but I am not sure. Here is a screen shot of the problem. (the app acts fine but this warning appears, I dont know if i should simply ignore it) or maybe I am doing something wrong here, need your help guys, thanx

https://user-images.githubusercontent.com/12644122/31925375-94b7c464-b89d-11e7-889d-e9d00866ff73.png
https://user-images.githubusercontent.com/12644122/31925386-9e9bf3c4-b89d-11e7-9eaa-65147879629f.png

You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub https://github.com/facebook/react-native/issues/12981#issuecomment-338874018, or mute the thread https://github.com/notifications/unsubscribe-auth/AHhM1oYWKrWSOZsm-8hjp4Q6qJSfqraUks5svW6-gaJpZM4Mf0Z0.

Usually I save into a redux store the userinfo.
So I simply resuse as needed.
... but ... in a similar situation, i've the same identical problem with the timeout

Same issue with react-native-signalr library (300000ms)

socket.io server options
pingTimeout (60000) + pingInterval (25000) <= 60000

Any update on how we should deal with this on Android? Still getting it, not using setTimeout either ...

Just to follow up my previous comment (from August) which I belive sums up the feelings of all the folks around here: we're ok with this AlarmManager thingy is just that we want to know the best approach to handle Long Lived Transactions

cc @shergin @cpojer @javache @hramos

A fix for react-native-signalr (ms-signalr-client) on Android is to override default pinginterval (300000):
connection.start({ pingInterval: 5000, jsonp: true, transport: ['webSockets', 'serverSentEvents', 'longPolling']}).done(() => { console.log('Now connected, connection ID =' + connection.id) })

Same issue with react-native-pomelo (80000ms)

console.ignoredYellowBox = ['Setting a timer'];

This is quite safe as a workaround as you will still get the warning in chrome's console when you debug remotely

Where exactly is the best place to put the console ignore code?'

-- nevermind, got a hint from the linked stackexchange, added to my App.js as follows:

class App extends Component {
  constructor() {
    super();

    console.ignoredYellowBox = [
      'Setting a timer'
    ];
  }
...
}
````

and also tried in my index.js as @haikyuu just recommended..

import { AppRegistry } from 'react-native';
import App from './App';

console.ignoredYellowBox = [
'Setting a timer'
];

AppRegistry.registerComponent('manager', () => App);
````

worked in both

@CopyJosh you can put it in your entry file: index.js

so there isn't any actual solution to this yet? it's been 8+ months now..

?

Let's get this solved then. We'd probably have to figure out:

A.) An easy extensible API that can be called via the React code. I think it would be wise to tackle maybe getting just one of AlarmManagers methods to work. For example, calling the following method in your React Native code would set an alarm with the android devices native AlarmManager.
AlarmMgr.setInexactRepeating(arg1, arg2...)

B.) The actual implementation of getting this to work. As far as I know, setting an alarm with AlarmManager sends what is called a "pending intent" to the AlarmManager service. You tell AlarmManager that you would like to execute some type of "task" in the future. It seems to me, that you would need to have AlarmManager(or a custom React Java Method) send some type of data back to your JavaScript code, telling it what code to execute after the alarm goes off.

Anybody up for tackling this problem? More can be read on AlarmManager here.

btw - is this inefficiency (related to long, multi-minute timeouts) a new issue only for RN versions > 0.43 ? Or is it just that the warning is being spit out from RN > 0.43? I migrated from 0.43.x to 0.47.x and started seeing the warning. I assumed that the inefficiency had always been there, and that RN has just started warning me about that now..

however, ever since the migration a few days ago, I also noticed that my app is consuming a lot more battery on my phone! (and the other features in the diff are not obvious culprits)

Hey @astreet, do you maybe still remember what is that you were referring to when posting this issue and adding this warning here https://github.com/facebook/react-native/commit/3637bce479 ?

It has recently became quite annoying to me after I started using one lib that sets a 2min recurring timer so I started to dig into it to understand why this warning has been added in the first place.

Now I am concerned about this sentence: "keeps the Timing module awake". What I think you may be referring to is that Timer module is setting choreographer callback. Although the callback seem to be set regardless of if we have the timer or not. See relevant code part here: https://github.com/facebook/react-native/blob/1e8f3b11027fe0a7514b4fc97d0798d3c64bc895/ReactAndroid/src/main/java/com/facebook/react/modules/core/Timing.java#L106
There is no code to stop callback when there are no timers scheduled and there is no code to start scheduling when timer gets added. The callback is suspended only when the app moves to background and restored when moved back to foreground.

As a consequence the choreographer callback is enqueued for each frame regardless of if we have a timer for a long period, short period or no timer at all set. I don't see how setting long timer can impact performance in that case. So does it mean then the warning isn't necessary?

As far as setting long timer being a "correctness issue" I think you are referring to the fact that timers will only be called in the foreground app state (so when you set it for some long time in the future and the app goes into bg it either won't execute, or will execute when the app gets into foreground which may be significantly later that you'd expect). Although I believe this is the same case for iOS where we don't show the warning

In socket.io the default ping loop is 85000ms, = pingInterval(25000ms) + pingTimeout(60000ms). and you should reset those options in server and make sure the sum of pingInterval and pingTimeout <= 60000ms.

for example:

var socket = require('socket.io')(12345, {
  pingInterval: 20000, 
  pingTimeout: 40000
})

Just curious - I need to be able to set an approximately 5 minute timeout in my app. How serious is this issue and how much would it affect me?

@ntomallen If the app will be on-screen all the time, then nothing will change. It'll work smoothly. Otherwise, maybe it won't trigger at the end of the 5 minutes. Or it'll do unexactly.

I use laravel echo with React Native on android change socket server options pingTimeout to 30000. It works

@sospedra gotcha. I'll have to do some testing to make sure it's not too bad!

Is there a reason to not always use Handler.postDelayed ?

any quick fix for this issue ? I just started to use RN with Firebase.

I've tried a few different timer solutions, but haven't had any luck getting one working in the background (consistently).

I've using a setInterval to ping my server every 10 minutes - I just noticed this error message because I do a setInterval(myFunction, 60000). Is this something I need to be worried about?

My expected behaviour is that while the app is open, it can send off the requests, but after the app is suspended (single home button press) the requests should stop firing. Is this the behaviour that I can expect, and will the battery life be impacted while the app is suspended?

There's nothing to worry about here, and the warning is incorrect and should be removed. There isn't a "Timing Module" to keep awake, there's just a Choreographer callback that checks for timers every frame (which also makes the min granularity for timers be ~16ms).

There is a correctness issue in the sense that timers won't fire in the background on either iOS or Android. This warning seems to be specifically about Android though and does not appear to be accurate. All timers should eventually fire when the app comes back into the foreground.

Any plans on having this addressed soon? The log is annoying anytime we're testing on Android phones.

My Problem…

I was getting this error because my timeout was up to ~59 minutes. The code to call a method on the top of the hour and had a setTimeout to create a setInterval at the next top of the hour.

My Solution…

  1. update the setTimeout to start the setInterval at the top of the minute.
  2. update the function called by the setInterval to only run the function if the current minutes was 0.

This function now runs every minute instead of every hour… but since it does nothing 59 times out of 60, seems harmless.

hey @beausmith, is this really a solution to simply set 60 timers? To my understanding, it is still keeping the timerModule active for the same hour, but isn't showing the warning :)

@newah - yeah, it's a little hacky… can you suggest a better solution?

@beausmith, I tried using https://github.com/ocetnik/react-native-background-timer which uses native timers which sounds like a solution - I've simply overwritten setTimout and setInterval with this lib's implementation.

Supporting timers in background might be a plus or minus depending on your needs :))

Seems to work fine, but didn't solve my problems though :)

I have same warning if I use react-intl FormattedRelative. Because inside component FormattedRelative is used timer:

scheduleNextUpdate(props, state) {
    // Cancel and pending update because we're scheduling a new update.
    clearTimeout(this._timer);

    const {value, units, updateInterval} = props;
    const time = new Date(value).getTime();

    // If the `updateInterval` is falsy, including `0` or we don't have a
    // valid date, then auto updates have been turned off, so we bail and
    // skip scheduling an update.
    if (!updateInterval || !isFinite(time)) {
      return;
    }

    const delta = time - state.now;
    const unitDelay = getUnitDelay(units || selectUnits(delta));
    const unitRemainder = Math.abs(delta % unitDelay);

    // We want the largest possible timer delay which will still display
    // accurate information while reducing unnecessary re-renders. The delay
    // should be until the next "interesting" moment, like a tick from
    // "1 minute ago" to "2 minutes ago" when the delta is 120,000ms.
    const delay =
      delta < 0
        ? Math.max(updateInterval, unitDelay - unitRemainder)
        : Math.max(updateInterval, unitRemainder);

    this._timer = setTimeout(() => {
      this.setState({now: this.context.intl.now()});
    }, delay);
  }

Here is example of use in my code:

<FormattedRelative value={date}>
    {(message: string) => <Text>{message}</Text>}
</FormattedRelative>

Anybody know how fix this warning?

This is still an issue. It's particularly annoying when using firebase services on android. Please fix.

This is a really bad bug for firebase services and when you are dependent on third part apps. Because firebase does not work if you switch between apps to verify something to continue the process.

An example is we use a thirdparty/factor login system, and using firebase to move tokens. But since we clean up the token is gone before the person is back in our app. So I can change the cleanup mechanis, or you guys could fix this bug.

@vongohren not that I understand your use case fully, but one workaround strategy might be to store the backgrounding date within your app on entry to the background, then on return to the foreground compare the current date with the stored backgrounding date and perform some cleanup if a certain time period has elapsed...

To elaborate on my use case. Say you are dependent on saying yes in another app to accept the login. Feks switch over to facebook app to say yes to a login request in a third party. The concrete example im working with is uPort, https://www.uport.me/. Then when we rely on a way to message our app, now in the background to say that all is good, this doesnt work with firebase unless we take your approach to clean up at a later point.

I guess I was just stuck on that the clean up could happen right after emitted information, becaues then the information was already emitted, and we would not need it. But why is it so that the firebase approach does not work in react native? That it stops its background workers.

I need a 30 minutes timeout in my app. Every time the user has interaction with the screen it will reset (throw away old timeout, create new one).

Any alternative on how to do this the right way? Is it better to handle this with native code and pass something to React when finished?

wow, so this still an issue

@nbennink check answer here . Currently there's no guarantee way to run task in the background since there are multiple factors in play (battery saver, user interaction etc.).

I also getting same warning after using socket.io.
Warning is below:

Setting a timer for a long period of time, i.e. multiple minutes, is a performance and correctness issue on Android as it keeps the timer module awake, and timers can only be called when the app is in the foreground. See https://github.com/facebook/react-native/issues/12981 for more info.
(Saw setTimeout with duration 85000ms)

My package details is below:

  • "react": "^16.3.0-alpha.1",
  • "react-native": "0.54.4",
  • "socket.io": "^2.1.1",
  • "socket.io-client": "^2.1.1",

Any permanent solution find out?

I also getting same warning after using socket.io.
Warning is below:

Setting a timer for a long period of time, i.e. multiple minutes, is a performance and correctness issue on Android as it keeps the timer module awake, and timers can only be called when the app is in the foreground. See https://github.com/facebook/react-native/issues/12981 for more info.
(Saw setTimeout with duration 85000ms)

My package details is below:

"react": "^16.3.0-alpha.1",
"react-native": "0.54.4",
"socket.io": "^2.1.1",
"socket.io-client": "^2.1.1",

Any permanent solution find out?

Ditto, there is no way to go around this?

Still getting this warning on react-native 0.57, is this issue supposed to be open instead?

Almost one year and React Native still have the same issue!!!

Facing the same issue, same warning appears 3-4 times. Very annoying.

Is there any solution to solve this issue?

Got rid of the warning message by this solution https://stackoverflow.com/a/48778011/7427111

It's basically just hiding the message not fixing the problem, I guess.

@dhavaljardosh Yes, your guess is right.

I created a native timer with Java and Objective C. It works for me and it hides the message. I'm not sure if it actually improves performance. As mentioned above: It is still bad for the battery etc. because there is a long timer in the background.

One question to ask is if you truly need a polling function. I thought I did, but then due to this issue, realized I only needed to execute on _some_ semi-frequent period of time. So now I'm running it on app background and totally avoiding this issue. YMMV.

same problem when using Pusher...

I will test my app to see what is the performance impact when setting a timer every 60 seconds.
I assume, however, that some of you have experience with that. Can you share this experience, please? Is the impact significant?

still same issue with latest version.
any solution?

I'm facing that issue too.
The app was working fine, it's only when I started using firebase that the warning came.
Any solution?

I want to understand the meaning behind this message as we've started getting it after a recent feature we've added to our product. Is the issue _only_ with the fact that the timer keeps running while the app is backgrounded? Would it be an appropriate solution to subscribe to app state events and stop the timer if the app is backgrounded? Or is a long-running timer with the app in the _foreground_ problematic as well?

I want to understand the meaning behind this message as we've started getting it after a recent feature we've added to our product. Is the issue _only_ with the fact that the timer keeps running while the app is backgrounded? Would it be an appropriate solution to subscribe to app state events and stop the timer if the app is backgrounded? Or is a long-running timer with the app in the _foreground_ problematic as well?

Would like to know this as well.

Furthermore, what happens with setInterval ? I don't get warnings from this one, but does it have the same issue?

I want to understand the meaning behind this message as we've started getting it after a recent feature we've added to our product. Is the issue _only_ with the fact that the timer keeps running while the app is backgrounded? Would it be an appropriate solution to subscribe to app state events and stop the timer if the app is backgrounded? Or is a long-running timer with the app in the _foreground_ problematic as well?

Yellow Message says "...and timers can only be called when the app is in the foreground."
Timer module stays awake but timers can't operate when the app is in the background? So the problem is keeping the timer module awake when an app is backgrounded? How much of a performance/battery effect would that have I wonder...

@BerndWessels https://www.npmjs.com/package/react-native-firebase Have you looked at this? Says it allows firebase to run on the native thread. Not sure how true that is, but might be worth a look. You might even be using that, I'm not sure.

Do you think If I switch to this library I will have to change my existing code? I mean, that would not be very nice...

same issue with Pusher on latest RN0.59, and ui may stuck a few seconds ...

I seem to only run into this issue when I make a call to firebase's realtime database.

await firebase.database().ref('users/' + id).set(data);

RN 0.57.1

I have not confirmed with 0.59.

@swushi I am getting the same issue with RN 0.59

I am getting following warning while using firebase library

Setting a timer for a long period of time, i.e. multiple minutes, is a performance and correctness issue on Android as it keeps the timer module awake, and timers can only be called when the app is in the foreground. See https://github.com/facebook/react-native/issues/12981 for more info. (Saw setTimeout with duration 111862ms)

How to get rid of this warning...

Does anyone know how to get rid of this warning without hiding it??
I use the firebase library not the react-native-firebase, but seems to have the same issue and I don't know how muh it will affect me when it gets to production.

I am really worried so if someone could help please...

I'm using the following code to get around this issue (at the moment):

see https://github.com/firebase/firebase-js-sdk/issues/97#issuecomment-427512040

fixtimerbug.js

/////////////////////////////////////////////////////////////////////////////
////// temporary fix to bug about 'Setting a timer' /////////////////////////
////// See: https://github.com/pusher/pusher-js/issues/248 //////////////////
////// See: https://github.com/facebook/react-native/issues/12981 ///////////
////// See: https://github.com/firebase/firebase-js-sdk/issues/97 ///////////
/////////////////////////////////////////////////////////////////////////////
import { Platform, InteractionManager } from 'react-native';
const _setTimeout = global.setTimeout;
const _clearTimeout = global.clearTimeout;
const MAX_TIMER_DURATION_MS = 60 * 1000;
if (Platform.OS === 'android') {
  const timerFix = {};
  const runTask = (id, fn, ttl, args) => {
    const waitingTime = ttl - Date.now();
    if (waitingTime <= 1) {
      InteractionManager.runAfterInteractions(() => {
        if (!timerFix[id]) {
          return;
        }
        delete timerFix[id];
        fn(...args);
      });
      return;
    }
    const afterTime = Math.min(waitingTime, MAX_TIMER_DURATION_MS);
    timerFix[id] = _setTimeout(() => runTask(id, fn, ttl, args), afterTime);
  };
  global.setTimeout = (fn, time, ...args) => {
    if (MAX_TIMER_DURATION_MS < time) {
      const ttl = Date.now() + time;
      const id = '_lt_' + Object.keys(timerFix).length;
      runTask(id, fn, ttl, args);
      return id;
    }
    return _setTimeout(fn, time, ...args);
  };
  global.clearTimeout = id => {
    if (typeof id === 'string' && id.startsWith('_lt_')) {
      _clearTimeout(timerFix[id]);
      delete timerFix[id];
      return;
    }
    _clearTimeout(id);
  };
}
/////////////////////////////////////////////////////////////////////////////

@cpmech
You implement that in an independent .js file, or you do it inside the script that makes the timer exception

Hi, yes the code above goes in a file named 'fixtimerbug.js'. Then, I load it in my App.js like so:

import './src/fixtimerbug'; // <<<<<<<<<<<<<<<<<<

import React from 'react';
import { Platform, View, StatusBar } from 'react-native';
import AppNavigator from './src/navigation/AppNavigator';
import Store from './src/model/Store';

const store = new Store();

const App = () => (
  <View style={{ flex: 1, backgroundColor: '#fff' }}>
    {Platform.OS === 'ios' && <StatusBar barStyle="default" />}
    <AppNavigator screenProps={{ store }} />
  </View>
);

export default App;

Seems that it is working fine, what does it do exacly?

Thank youu very much @cpmech

Hi, the code (from here: https://github.com/firebase/firebase-js-sdk/issues/97#issuecomment-427512040) simply wraps the setTimeout (global) function to prevent it from being used with a long period.

In our modified global.setTimeout function, if the duration time is greater than the threshold MAX_TIMER_DURATION_MS, we save an id in a local map (timerFix) and then call runTask which splits the duration time into smaller values. runTask sets a timeout (using the original _setTimeout) with the smaller chunks that will be continuously executed until waitingTime is too small. When waitingTime is small enough, we call React Native's runAfterInteractions for a smooth execution, and, at the same time, we remove the id entry from our local map because we won't be calling _setTimeout again in this case. Note that afterTime is fed into _setTimeout to prevent it from being called with a long period.

Otherwise, in our modified global.setTimeout function, if the duration time is smaller than the threshold MAX_TIMER_DURATION_MS, we directly call the global setTimeout (saved in _setTimeout).

The global clearTimeout is also wrapped to perform a cleanup process by removing the id from the local map.

Hope it helps!

Facing the same issue.. Seems like, we'd have to hide the warning for the time being. Here's the shortest way to do it:
componentDidMount() { console.disableYellowBox = true; ... }

Hi, the code (from here: firebase/firebase-js-sdk#97 (comment)) simply wraps the setTimeout (global) function to prevent it from being used with a long period.

In our modified global.setTimeout function, if the duration time is greater than the threshold MAX_TIMER_DURATION_MS, we save an id in a local map (timerFix) and then call runTask which splits the duration time into smaller values. runTask sets a timeout (using the original _setTimeout) with the smaller chunks that will be continuously executed until waitingTime is too small. When waitingTime is small enough, we call React Native's runAfterInteractions for a smooth execution, and, at the same time, we remove the id entry from our local map because we won't be calling _setTimeout again in this case. Note that afterTime is fed into _setTimeout to prevent it from being called with a long period.

Otherwise, in our modified global.setTimeout function, if the duration time is smaller than the threshold MAX_TIMER_DURATION_MS, we directly call the global setTimeout (saved in _setTimeout).

The global clearTimeout is also wrapped to perform a cleanup process by removing the id from the local map.

Hope it helps!

Thank you so much! Worked like a charm.

Hi, the code (from here: firebase/firebase-js-sdk#97 (comment)) simply wraps the setTimeout (global) function to prevent it from being used with a long period.

In our modified global.setTimeout function, if the duration time is greater than the threshold MAX_TIMER_DURATION_MS, we save an id in a local map (timerFix) and then call runTask which splits the duration time into smaller values. runTask sets a timeout (using the original _setTimeout) with the smaller chunks that will be continuously executed until waitingTime is too small. When waitingTime is small enough, we call React Native's runAfterInteractions for a smooth execution, and, at the same time, we remove the id entry from our local map because we won't be calling _setTimeout again in this case. Note that afterTime is fed into _setTimeout to prevent it from being called with a long period.

Otherwise, in our modified global.setTimeout function, if the duration time is smaller than the threshold MAX_TIMER_DURATION_MS, we directly call the global setTimeout (saved in _setTimeout).

The global clearTimeout is also wrapped to perform a cleanup process by removing the id from the local map.

Hope it helps!

Does this actually address the issue or does it just remove the warning?

Hi, the code (from here: firebase/firebase-js-sdk#97 (comment)) simply wraps the setTimeout (global) function to prevent it from being used with a long period.

In our modified global.setTimeout function, if the duration time is greater than the threshold MAX_TIMER_DURATION_MS, we save an id in a local map (timerFix) and then call runTask which splits the duration time into smaller values. runTask sets a timeout (using the original _setTimeout) with the smaller chunks that will be continuously executed until waitingTime is too small. When waitingTime is small enough, we call React Native's runAfterInteractions for a smooth execution, and, at the same time, we remove the id entry from our local map because we won't be calling _setTimeout again in this case. Note that afterTime is fed into _setTimeout to prevent it from being called with a long period.

Otherwise, in our modified global.setTimeout function, if the duration time is smaller than the threshold MAX_TIMER_DURATION_MS, we directly call the global setTimeout (saved in _setTimeout).

The global clearTimeout is also wrapped to perform a cleanup process by removing the id from the local map.

Hope it helps!

Thanks a lot! It won't effect the overall performance of the app right?

Hi, the code (from here: firebase/firebase-js-sdk#97 (comment)) simply wraps the setTimeout (global) function to prevent it from being used with a long period.
In our modified global.setTimeout function, if the duration time is greater than the threshold MAX_TIMER_DURATION_MS, we save an id in a local map (timerFix) and then call runTask which splits the duration time into smaller values. runTask sets a timeout (using the original _setTimeout) with the smaller chunks that will be continuously executed until waitingTime is too small. When waitingTime is small enough, we call React Native's runAfterInteractions for a smooth execution, and, at the same time, we remove the id entry from our local map because we won't be calling _setTimeout again in this case. Note that afterTime is fed into _setTimeout to prevent it from being called with a long period.
Otherwise, in our modified global.setTimeout function, if the duration time is smaller than the threshold MAX_TIMER_DURATION_MS, we directly call the global setTimeout (saved in _setTimeout).
The global clearTimeout is also wrapped to perform a cleanup process by removing the id from the local map.
Hope it helps!

Does this actually address the issue or does it just remove the warning?

No it doesn't address the issue. It's just a good work around.

Hi, the code (from here: firebase/firebase-js-sdk#97 (comment)) simply wraps the setTimeout (global) function to prevent it from being used with a long period.
In our modified global.setTimeout function, if the duration time is greater than the threshold MAX_TIMER_DURATION_MS, we save an id in a local map (timerFix) and then call runTask which splits the duration time into smaller values. runTask sets a timeout (using the original _setTimeout) with the smaller chunks that will be continuously executed until waitingTime is too small. When waitingTime is small enough, we call React Native's runAfterInteractions for a smooth execution, and, at the same time, we remove the id entry from our local map because we won't be calling _setTimeout again in this case. Note that afterTime is fed into _setTimeout to prevent it from being called with a long period.
Otherwise, in our modified global.setTimeout function, if the duration time is smaller than the threshold MAX_TIMER_DURATION_MS, we directly call the global setTimeout (saved in _setTimeout).
The global clearTimeout is also wrapped to perform a cleanup process by removing the id from the local map.
Hope it helps!

Thanks a lot! It won't effect the overall performance of the app right?

No, it doesn't affect the performance at all.

A quick note on

If it bothers you just add console.ignoredYellowBox = ['Setting a timer'];

and

Facing the same issue.. Seems like, we'd have to hide the warning for the time being. Here's the shortest way to do it:
componentDidMount() { console.disableYellowBox = true; ... }

This is not a workable solution. The errors still happen, you just don't get a notification.

Result? The error parsing still needs to happen.

In the case of using Firebase listeners for example you will be hiding a large number of errors, but your development environment will still be handling them.

Case in point: my expo based project kept crashing because it was trying to sourcemap every timer error.

I'm closing this because I don't think that React Native will add support for AlarmManager in the core. It can be done with third-party modules or custom native code.

@cpmech 's solution works yet? I tried it on RN60.4, but getting long timer warning yet.

Hi, I think I found the solution:
Firstly you have to find the following file in your project: libraries/Core/Timers/JSTimer;js
Open it and you just have to change this const MAX_TIMER_DURATION_MS, to increase above your duration, wrote in the end of warning !

increase the timer , but how much ?

@dulmandakh are there any open source projects that solve this issue that you know of ?

@dulmandakh are there any open source projects that solve this issue that you know of ?

RNFirebase maybe? @dulmandakh

You can reschedule the timer with a fixed time until it expires:


const setLogoutTimer = expirationTime => {

  return dispatch => {
    timer = setTimeout(() => {
        if (expirationTime>60000)
        {
            console.log(`set new exp:${expirationTime-60000}`);
            dispatch(setLogoutTimer(expirationTime-60000));
        }
        else
        {
            console.log('logout');
            dispatch(logout());
        }
    }, 60000); 
  };
};

It's been 2 years will react-native or firebase fix this issue properly. :(

ok after a few hours of long investigation , all i want to know is if I caused the issue somehow.
I'm not sure when it appeared.
does it pop up as soon as i call firestore?
I'm not using any timers in my app

@luismasg I do not use setTimeout in my code as it is bad practice but i know the source of the error and I can't stop it and it seems it come from establishing a remote connection with my database apparently the library I am using sets the Time out to keep the TTL alive , check your dependency library and see if you can identify the one causing the issue

Honestly, I don't think using JS timers/intervals is a good idea for long timers anyways, the warning is there for a reason.

Use lifecycle events to add/remove short timers instead. If you need actual background execution, use native code with foreground services (Android) and background tasks (iOS).

Honestly, I don't think using JS timers/intervals is a good idea for long timers anyways, the warning is there for a reason.

As mentioned expansively above, many people struggle with libraries that use longer timers. "Just don't use firebase" is not a solution.

Obviously this is an often encountered issue, handling it would improve the developer experience of react-native which sounds like it's ultimate goal...

I wouldn't say "don't use firebase" is the answer, more like, "fix firebase to not use long timers and use the appropriate native building blocks for w/e task they are using long timers for".

But you are right, it would be ideal for RN to support long timers somehow, but considering all the caveats there are (like higher battery consumption, unnecessary background execution time, etc.), it will just make developers shoot themselves on the foot. If anything, it should be a separate timer/interval primitive.

If anything, it should be a separate timer/interval primitive.

My understanding of what this issue is about is the lack of such a primitive 😄

use the timer inside the timer , thats all !

@dulmandakh why is this issue closed? The problem persists and discussion is ongoing?

I added this to App.js, and this fixed it (temporary until there is an official fix)

const _setTimeout = global.setTimeout;
const _clearTimeout = global.clearTimeout;
const MAX_TIMER_DURATION_MS = 60 * 1000;
if (Platform.OS === "android") {
  // Work around issue `Setting a timer for long time`
  // see: https://github.com/firebase/firebase-js-sdk/issues/97
  const timerFix = {};
  const runTask = (id, fn, ttl, args) => {
    const waitingTime = ttl - Date.now();
    if (waitingTime <= 1) {
      InteractionManager.runAfterInteractions(() => {
        if (!timerFix[id]) {
          return;
        }
        delete timerFix[id];
        fn(...args);
      });
      return;
    }

    const afterTime = Math.min(waitingTime, MAX_TIMER_DURATION_MS);
    timerFix[id] = _setTimeout(() => runTask(id, fn, ttl, args), afterTime);
  };

  global.setTimeout = (fn, time, ...args) => {
    if (MAX_TIMER_DURATION_MS < time) {
      const ttl = Date.now() + time;
      const id = "_lt_" + Object.keys(timerFix).length;
      runTask(id, fn, ttl, args);
      return id;
    }
    return _setTimeout(fn, time, ...args);
  };

  global.clearTimeout = id => {
    if (typeof id === "string" && id.startWith("_lt_")) {
      _clearTimeout(timerFix[id]);
      delete timerFix[id];
      return;
    }
    _clearTimeout(id);
  };
}

This is a workaround, not a fix.

I added this to App.js, and this fixed it (temporary until there is an official fix)

...
    if (typeof id === "string" && id.startWith("_lt_")) {
...

'startWith' should be 'startsWith' if you're going to use that code.

I'm experiencing this with AWS Amplify Datastore. In lieu of a proper fix, I'm disabling the warning with

import { YellowBox } from 'react-native';
YellowBox.ignoreWarnings(['Setting a timer']);

It's not clear to me whether the underlying issue is something that RN needs to fix, Amplify needs to fix, or is something that I need to patch. Obviously I don't want performance issues.

If it bothers you just add console.ignoredYellowBox = ['Setting a timer'];

Where should I add this I tried in Screen file and action file, still not working

Just put it somewhere in your App.js file. I put it in my constructor for example.
Don't know if that's the best place, so if anyone knows anything better, I'd like to hear it!

I think whoever is using Firebase can solve this problem configuring the persistence of app after (signIn / signUp)

*after create a new user, he will be automatically sign in

https://firebase.google.com/docs/auth/web/auth-state-persistence

so... after auth a user, a time variable is created.

the default for firebase is a local. "firebase.auth.Auth.Persistence.LOCAL"

you can change using:
firebase.auth.Auth.Persistence.SESSION
firebase.auth.Auth.Persistence.NONE

or using a localStorage...

Until they solve it in a definitive way...

import { YellowBox } from "react-native";
import _ from "lodash";
YellowBox.ignoreWarnings(["Setting a timer"]);
const _console = _.clone(console);
console.warn = (message) => {
if (message.indexOf("Setting a timer") <= -1) {
_console.warn(message);
}
};

Once solved, the junk code is removed and voila!

This can be solved with RxJS.

Basically splits your 5200 timer into 5 * 1000 + 200 timers.

import { of, range } from 'rxjs';
import {
  concatMap,
  delay,
  filter,
  mapTo,
  switchMap,
  tap,
} from 'rxjs/operators';

// ~ setTimeout(..., 5200);

const source = of(5200).pipe(
  switchMap(duration => {
    const times = Math.floor(duration / 1000); // = 5
    const remainder = duration % 1000; // = 200

    return range(1, times).pipe(
      concatMap(i => of(i).pipe(delay(1000))),
      tap(console.log),
      filter(i => i === times),
      delay(remainder),
      tap(console.log),
      mapTo('Done !'),
    );
  }),
);

source.subscribe(console.log);

1 // After 1s
2 // After 2s
3 // After 3s
4 // After 4s
5 // After 5s
5 // After 5s + 200 ms
'Done !'

I think whoever is using Firebase can solve this problem configuring the persistence of app after (signIn / signUp)

*after create a new user, he will be automatically sign in

https://firebase.google.com/docs/auth/web/auth-state-persistence

so... after auth a user, a time variable is created.

the default for firebase is a local. "firebase.auth.Auth.Persistence.LOCAL"

you can change using:
firebase.auth.Auth.Persistence.SESSION
firebase.auth.Auth.Persistence.NONE

or using a localStorage...

Great! How about for Firestore though?

I think whoever is using Firebase can solve this problem configuring the persistence of app after (signIn / signUp)
*after create a new user, he will be automatically sign in
https://firebase.google.com/docs/auth/web/auth-state-persistence
so... after auth a user, a time variable is created.
the default for firebase is a local. "firebase.auth.Auth.Persistence.LOCAL"
you can change using:
firebase.auth.Auth.Persistence.SESSION
firebase.auth.Auth.Persistence.NONE
or using a localStorage...

Great! How about for Firestore though?

if you are using the old firebase SDK migrate to the firestore admin SDK.

most issues will be solved

https://firebase.google.com/docs/reference/admin

Still nobody is able to solve this issue. #shame on you developers ;p

Still nobody is able to solve this issue. #shame on you developers ;p

Never shame open source contributors. It's a shame this has not been fixed but the source is open, if you know how to solve it I'm sure they would appreciate a pull-request.

That said why on earth is this issue closed.

Because it should be fixed in the offending libraries. And the offending libraries don't see a problem with the way it has been implemented.

So nobody will fix it because both sides don't think it's broken.

They can be shamed a _little bit_ for this 😄

Guys, I think this hasn't been "resolved" for the simple reason that it is an extremely bad idea to use long timers.

If you want to run timers in background / events in the future, you will need a native library to do so. This is also specifically difficult on both platforms due to background execution constraints.

Based on @eightyfive 's comment we made a drop in replacement for rxjs6 delay, timeout & interval functions.

Still a question remains, can someone explain if the "Timer module" stays locked when using smaller intervals? Basically just causing the timer to halt when the app is not in the foreground. Or does this just hide the problem?

import { switchMap, concatMap, delay as nativeDelay, mapTo, filter, switchMapTo, repeat } from 'rxjs/operators'
import { range, of, race, throwError, TimeoutError } from 'rxjs'

const TIMER_INTERVAL = 1000
const reactNativeTimer = (duration) => {
  const times = Math.floor(duration / TIMER_INTERVAL)
  const remainder = duration % TIMER_INTERVAL
  if (times < 1) {
    return of(true).pipe(nativeDelay(remainder))
  }
  return range(1, times).pipe(
    concatMap(i => of(i).pipe(nativeDelay(TIMER_INTERVAL))),
    filter(i => i === times),
    nativeDelay(remainder)
  )
}

/**
 * React Native compatible version of delay pipe
 * @param {number} duration in ms
 */
export const delay = (duration) => {
  return (source) => {
    return source.pipe(
      switchMap(next =>
        reactNativeTimer(duration).pipe(mapTo(next))
      )
    )
  }
}

/**
 * React Native compatible version of timeout pipe
 * @param {number} duration in ms
 */
export const timeout = (duration) => {
  return (source) => {
    const timeoutTimer = reactNativeTimer(duration).pipe(
      switchMapTo(throwError(new TimeoutError()))
    )
    return race(source, timeoutTimer)
  }
}

/**
 * React Native compatible version of interval
 * @param {number} duration in ms
 */
export const interval = (duration) => {
  return reactNativeTimer(duration).pipe(
    repeat()
  )
}

The funny thing about it, that everyone in this thread discussing workaround to ignore this warning.
When I was redirected to this "issue" from warning, I hoped to find proper documentation, explaining "how?" to handle it and a bit more reasoning about "why?".

front-end #millennials

This #millenial would like to point out that:

  • This error does not require documentation, it is self explanatory: you set a long timer.
  • This thread is full of people muting the warning for libraries they do not own (e.g. firebase)
  • As the (productive) discussion in this thread indicates, this is a more complex issue to fix within react-native

That said I will again point out this issue should not be closed, it is marked known-issue and Help Wanted.

Millenial answers to millenial :)

This error does not require documentation, it is self explanatory: you set a long timer.

  • Its not an error, its warning (don't ask me about difference, otherwise we have to start deep philosophical conversation :) )
  • Its already pretend to be documented due to provided reference to this ticket, where I need to read tons of comments instead of executive summary (smth like known issue as you already suggested):
    image
  • I would not say it 'self explanatory'. Not enough context provided. Literally, there are two sentences in a description.

That said I will again point out this issue should not be closed, it is marked known-issue and Help Wanted.

Fully agree. It will save time to many people coming from the URL reference in the warning.

For firebase/firestore users: I simply reverted to using the REST endpoints directly for firestore calls. Since I only needed auth and firestore, that solved my problems rather easily. Snippet here:

https://stackoverflow.com/a/62446792

I did it inside App() as follows:

import { YellowBox } from 'react-native';
export default function App(){
YellowBox.ignoreWarnings(['Setting a timer']);
...
...
..
}

Remeber

What I did and it's working with me but still I don't knwo if it's a good practice or not

Navigated to file

node_modulesreact-native\Libraries\Core\TimersJSTimers.js

there is a function const MAX_TIMER_DURATION_MS = 60 * 1000 and I increased the time to be 60 * 100000 and it stopeed appearing

Hey there,
This issue is about 3 years old and we still having this warning ;)
as @RWOverdijk said ignoring the warning is not a solution.
Is there already a solution that i missed?

Thanks <3

Hey all, we're deciding to lock this issue. I know this problem is frustrating for many of you and I want to take the time to help understand why we're making this decision.

How timers work on Android in React Native

React Native has a custom implementation of JS timers like setTimeout. On Android, the way it works is that JS timers get tracked on the native side, and the native side decides when to trigger these timers. This lets React Native be smarter about when timers are triggered, and in particular how they relate to the app lifecycle and rendering.

What happens if I set a timer with a long interval?

Here's my understanding, from reading the code and testing it out a bit:

  1. If you set a long timer, and the app is kept open over the course of the timer being active, the timer should still work.
  2. If you set a long timer, and the phone is backgrounded before the timer finishes, the timer will not be activated until you next open the app.
  3. There is an exception for headless JS tasks, which can keep timers working even if the app is in the background.

Why shouldn't we call timers with long intervals?

I think this is primarily a correctness issue, and less about performance. The issue is that you can't rely on long timers if the user backgrounds your app.

If you don't mind having your timer get activated later when the app is foregrounded again, then I think ignoring the YellowBox warning is a good idea.

On the other hand, if your timer expects to be called for the lifetime of a session, and shouldn't be triggered again on foreground, you'll need to do something to make sure the timer is ignored on foreground.

How do I use third-party libraries like Socket.io and Firebase that set long timers?

I don't know the details of these third-party libraries. It ultimately comes down to the question above. If these third-party libraries will have issues dealing with timers being resolved on foreground, then you'll need to figure out a way to fix that. Some comments above propose ways of doing this for Firebase, for instance. Ultimately, though, you may be better served by relying on a library that's developed specifically with React Native in mind.

On the other hand, if the third-party library has no issues with timers being resolved on foreground, then you can ignore the error.

Why can't setTimeout go through AlarmManager, as suggested initially?

AlarmManager will wake up the app if it's in the background, and we don't think timers like setTimeout should wake up the app. Most of these long timers are set for things like timeouts, and those are only relevant while the app is open. Waking up an app in the background is expensive and should only be done if the developer explicitly has this intention.

At the same time, many people probably have a need for something like this. We could develop an API for it that doesn't go through setTimeout. But because of the Lean Core effort right now we're actually trying to avoid increasing the API surface for React Native. We think this is a task best handled by a third-party library like react-native-background-timer.

Why don't you remove the warning entirely then?

We want developers to be aware of what's going on. If somebody installs a library that relies on the correctness of setTimeout, we think the developer should be aware that we cannot guarantee this correctness. We want to leave it up to individual developers to make the decision of whether this warning matters in their particular use case.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

grabbou picture grabbou  ·  3Comments

upbit picture upbit  ·  3Comments

despairblue picture despairblue  ·  3Comments

jlongster picture jlongster  ·  3Comments

lazywei picture lazywei  ·  3Comments