Safari JavaScript setTimeout stops when minimized
Asked Answered
A

1

7

This is related to the following:
Safari 9.1 for El Capitan
Safari 10 for Sierra

Does anyone know what is Safari doing in the JavaScript engine with respect to setTimeout() when the browser or the tab loses focus (mainly, is minimized)?

I have created a simple JavaScript web application that I load in Safari that calls JavaScript setTimeout() that passes another function to be executed after the timeout value. The function prints the date/time to the console, then calls setTimeout() with the same timeout value.

If the Safari tab loses focus or the browser is minimized, after some time it seems Safari stops executing the setTimeout, and the subsequent function to be called, and until focus is returned, no function execution occurs. It's as if the event loop stops processing.

NOTE: No function calls are lost, they are only paused, and restarted when the browser regains focus.

I do not notice this in Firefox, Chrome or IE11.

Main reason this question is being posed is that the problem is mainly observed with a web application I am maintaining that uses CometD for communication back to the server. CometD library is using setTimeout to execute a request back to the server every 30 seconds. If the browser running the application is minimized, it seems CometD stops communicating with the server until the browser is maximized again.

Airborne answered 20/1, 2017 at 16:25 Comment(1)
While your question isn't a duplicate, the answer you are searching for is found here. Short answer: it's a new feature in webkit, to save energy on inactive tabs.Gorse
G
2

You should probably use setInterval instead, since according to this answer, setInterval still works, even if it is limited to 1 per second.

The setInterval() method of the WindowOrWorkerGlobalScope mixin repeatedly calls a function or executes a code snippet, with a fixed time delay between each call. Returns an intervalID.

Syntax var intervalID = scope.setInterval(func, delay[, param1, param2, ...]); var intervalID = scope.setInterval(code, delay); Parameters

func A function to be executed every delay milliseconds.

code An optional syntax allows you to include a string instead of a function, which is compiled and executed every delay milliseconds. This syntax is not recommended for the same reasons that make using eval() a security risk.

delay The time, in milliseconds (thousandths of a second), the timer should delay in between executions of the specified function or code. If this parameter is less than 10, a value of 10 is used. Note that the actual delay may be longer; see "Reasons for delays longer than specified" in WindowOrWorkerGlobalScope.setTimeout() for examples.

param1, ..., paramN Optional Additional parameters which are passed through to the function specified by func once the timer expires.


Timeouts in inactive tabs clamped to >=1000ms

To reduce the load (and associated battery usage) from background tabs, timeouts are often clamped to firing no more often than once per second (1000 ms) in inactive tabs.

Gorse answered 20/1, 2017 at 16:49 Comment(3)
+1 I see no difference between setInterval and setTimeout, and even the answer you link to does not call out any differences. In a test I ran, I simply see Safari continually increase the delay in executing, not remaining at the >=1000ms value, or any consistent value, but rather, the value increases from 2s, to 10, to over a minute at times. I do not observe this in Chrome or Firefox for example. So my question is whether anyone knows why is Safari so different, and in understanding what it is doing, perhaps learn of a workaround.Airborne
@Airborne This is the exact behavior I just noticed. It's causing my websockets to timeout and not reconnect which is infuriating to say the least. Apple need to do something about this.Muumuu
@Airborne I should have added: Safari not only caps timeout intervals at 1000ms, it adds an exponential delay to every single iteration.Muumuu

© 2022 - 2024 — McMap. All rights reserved.