electron: setTimeout not working reliably
- Electron version: 1.3.5
- Operating system: OS X
This is going to sound ridiculous, but setTimeout is not working for me in the main process. Consider this trivial application, no windows or anything:
var Electron = require( 'electron' );
var app = Electron.app; // Module to control application life.
// This method will be called when Electron has done everything
// initialization and ready for creating browser windows.
app.on('ready', function() {
var x = 0;
var timeout_tester = function run_again(){
console.log( x, x, x );
x += 1;
setTimeout( run_again, 30 );
};
timeout_tester();
});
When I run this, I see the counter increment on the console until ~900. From then on it very sporadically fires. I cannot tell you how long it took me to track an issue I was having down to this, and even then I did not believe it. Please tell me someone can reproduce this or point out the wildly stupid thing I am doing.
Thanks in advance
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Reactions: 11
- Comments: 39 (15 by maintainers)
Commits related to this issue
- fix electron timeout issue https://github.com/electron/electron/issues/7079 — committed to igdmapps/igdm by ifedapoolarewaju 6 years ago
- The app would sporadically have giant timimg issues affecting delays in actions, and visual updates of buttons. Fixed with a drop-in replacement of timers from https://github.com/electron/electron/iss... — committed to bitfocus/companion by haakonnessjoen 6 years ago
The problem isn’t milliseconds. It’s off by orders of magnitude. Something that’s supposed to respond in 500ms can take 5-10 seconds sometimes.
Just pass the command line argument “–disable-background-timer-throttling” (see https://peter.sh/experiments/chromium-command-line-switches/)
For anyone still having this issue, I realized that VSCode was adding in
import { setTimeout } from 'timers';automatically and causing this issue. I removed that and problem solved.
We had a simple multi-timer app using
setIntervaland noticed it was significantly slow when the tab was minimized or not in focus. AddingmainWindow.webContents.setBackgroundThrottling(false);seemed to have fixed it.I don’t think it would be ideal to encourage users to do this.
Not sure if the
child_process.forkwould work as I haven’t tested. As @dregenor commented, in our project we use a simple C++ program to wake the main process with stdout messages(similar to @brianmmorton solution, but cross platform). We chose to do this because a new electron process would consume a lot more memory, and our requirements are simple.I’m still not 100% sure this is a chromium bug, I assume it is because it seems to affect
setTimeoutof plain chrome renderers(if the tab is in background). If there’s no hope of fixing this in the short term with a libchromiumcontent patch, maybe we could use a similar approach as a short term fix by replacing electron’ssetTimeout/setIntervalwith versions that use the hack on behalf of the users, and if/when we manage to fix in chromium, revert it.Timer in renderer process is not working reliably , too.
contents.setBackgroundThrottling(allowed) allowed Boolean
An important thing to remember here is that
setTimeoutis NOT guarunteed to fire exactly the provided number of milliseconds after calling.See this section of the MSDN docs for more information https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout#Reasons_for_delays_longer_than_specified
In particular this bit
Even in Chrome and firefox my
setTimeoutcalls are a fewmsmost of the time.That being said, this does seem like quite a large jump from “just a few ms”. Perhaps Electron’s queue is generally a lot busier 😕
Here’s a gist with our code copied / pasted with the fixes for setTimeouts.
https://gist.github.com/brian-mann/94429cba31ec3a17b08649072bf15578
I’ll be happy to point users directly to how it’s integrated into Cypress once we open source in the next week or two.
This works by spawning a forked process and using ipc with the child process (which will be spawned in node, not electron) and is not susceptible to busted timers.
You then override all of the global timers (first thing in your code) and create a lightweight queuing mechanism that reimplements setTimeout, clearTimeout, setInterval, and clearInterval.
We disable node-integration so this was only a problem outside the renderer process for us. It sounds more complicated than it really was to setup.
I can confirm that this 100% fixes the problem.
Here is a potential solution that works for my use case if anyone sees it fit:
Create a bash script, timer.sh
Most likely you’ll want to sleep $1; at the start of the process too
Execute that using spawn and take in the stdout to use as the timer