socketio / engine.io-client

The engine used in the Socket.IO JavaScript client, which manages the low-level transports such as HTTP long-polling, WebSocket and WebTransport.

Home Page:https://socket.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

setTimeout to emit drain in websocket.js:175 causes following message delayed 1s when tab in background

zyf0330 opened this issue · comments

You want to:

  • [] report a bug
  • [x ] request a feature

Current behaviour

When using Chrome or Safari and putting tab into background, timer is delayed to ensure at least 1s interval. And it causes that
later message is delayed 1s.

Steps to reproduce (if the current behaviour is a bug)

  1. write a simple client which sends two messages to server every 1s and run in browser
  2. put browser tab into background
  3. put it back into foreground

Expected behaviour

sent message is not delayed

Setup

  • OS: Ubuntu
  • browser: Chrome
  • engine.io-client version: 4.0.5

Other information (e.g. stacktraces, related issues, suggestions how to fix)

I try to remove this setTimeout but connection is disconnected. So I want to know if setTimeout can be removed?

Hi! Here's what I have been able to reproduce:

// client-side
const socket = io();

let count = 0;

setInterval(() => {
  socket.emit("test", ++count, new Date());
}, 1000);

// server-side
io.on("connection", (socket) => {
  socket.on("test", (count, date) => {
    console.log(count, date.substring(11), new Date().toISOString().substring(11));
  })
});
7 21:33:19.556Z 21:33:19.558Z
8 21:33:20.556Z 21:33:20.558Z
# put browser tab to background
9 21:33:22.385Z 21:33:22.386Z
10 21:33:23.385Z 21:33:23.386Z
11 21:33:24.385Z 21:33:24.386Z

Happens on Chrome (87.0.4280.88) and Firefox (84.0) / Ubuntu.

Is this the behavior you are describing? In that case, since the timestamp is the same on the client and on the server, I'd say the issue comes from the setInterval in the test, which is delayed when the tab is put to background.

Yes, this is the case.
Delay is caused by setTimeout here

setTimeout(function() {
.
I want to know if it can be optimized.

Hello!

We've ran into a similar time throttling issue where the server or the client will get ping timeout because of a missed heartbeat. We're still on socket.io v2 but looking at the code I think the newer version could suffer from the same issue.

From the client perspective, after the tab has been backgrounded for around 5 minutes the setTimeout of zero in the write method does not get fired until many, many seconds later resulting in the ping packet not being sent to the server in time before the client times out (the timeout timer is started before the packet is sent not when the packet is flushed). The ping packet gets stuck in the buffer because the writable was not set to true. I suspect the new version will have the same issue but instead of ping, the response will not be sent back to the server in time.

Our workaround is to change the setTimeout to a Promise.resolve().then() which will still block writes until the next tick without Chrome throttling the timer to death. We have good results so far.

We've also had a hard time reproducing this issue because every time we tried to create a small test the timer does not seem to throttle. I suspect Chrome only aggressively throttles on tabs that are consuming higher resources.

@darrachequesne With Chrome now throttling setTimeout calls and other browsers soon following suit, can the setTimeout be removed? Why is it useful here?

The setTimeout() call seems to be useful though, because the CI fails for 32702ee.

But I don't mind removing it if you find an elegant way to do so 👍

For reference, it was added in f3710ff.

This should be fixed by f30a10b, included in engine.io-client@5.1.2.

Big thanks to @hyperlink for his PR 👍