I want to write firestore database every 15 seconds. This is the code I have right now:
void startTimer() {
const oneSec = const Duration(seconds: 15);
_timer = new Timer.periodic(
oneSec,
(Timer timer) async {
await games.doc(documentNumber).update({
'players': FieldValue.arrayUnion([player.toString()]),
})
},
);
}
Then I call startTimer() in my build function. It writes data to the database, but the performance is weird. Instead of writing the database every 15 seconds, it does it almost every second, and then the number of writes is unpredictable (more than one), which then leads to a memory leak. I don’t know what and where to troubleshoot, because I don’t see any problem in my code
How can I make it write exactly 1 entry to the database every 15 seconds?
2
Answers
I would say that using an asynchronous method inside a
Timer
could be the problem, However since you want to execute your method every 15 seconds, think about making a recursion method, withFuture.dalayed
like this:this is a recursion method, that will execute your
update()
method every 15 seconds, and will stop from continuing after 10 times it’s run, If you want it to not stop in your app, just setshouldContinueWorking = true
.I can’t say for sure, but from the context you’ve given in your question I think I have a good idea of what’s going on.
const oneSec = const Duration(seconds: 1);
– either that or your variable naming needs some workSo there’s a few things going on here. The most obvious issue is that the widget’s build function can and will run more than once in almost all applications. You should never rely on it not running more than once – that’s not what it is meant for.
Because you initially started your widget with a timer running once a second, that will continue to run indefinitely. If you then do multiple build updates, that would then create more timers which also run indefinitely – which is exactly why you were seeing the number of writes grow as well as the unexpected timing.
Instead, you should be making your widget into a StatefulWidget and initiating the timer in an overridden
initState()
method (which will only run once per widget but should not access thecontext
, which is fine in your case).This would look like this:
If you did need to access the context, you’d instead need to use an overridden
didChangeDependencies()
method which can run more than once but likely won’t unless you’re using inherited widgets. In this case you would need to put in some protections to make sure the timer wasn’t created more than once i.e. make it nullable and check if it is set before creating a new one.Or if you really do need to call the startTimer() function from your build function, you could do it the same way as mentioned above – make
_timer
nullable and check if it is set before making a new one. But I’d recommend avoiding this – if you always keep things that are started in initState and then always stop them in dispose, it will be a lot easier to figure out what’s going on in the class.