question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Using await within emit causes payload to be sent to all connected clients

See original GitHub issue

You want to:

  • report a bug
  • request a feature

Current behaviour

Using async/await to construct payloads results in a race condition and messages potentially being broadcast to all connections.

Steps to reproduce

Fiddle: https://github.com/tommoor/socket.io-fiddle/tree/async-await-critical-issue TLDR: Using await within the payload construction for emit will cause the message to be sent to all connections instead of the specific room. eg:

io.to('my-private-room').emit('event', {
  data: await myDatabaseQuery()
});

Expected behaviour

Data should be sent to the correct room and not leaked to all clients.

Setup

  • OS: All
  • browser: All
  • socket.io version: 2.2.0

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:8
  • Comments:21 (3 by maintainers)

github_iconTop GitHub Comments

8reactions
ralphpigcommented, Jun 14, 2019

@tommoor Not sure why this issue was highjacked but I’ve been dealing with an issue like this for the past few days.

The problem isn’t with async/await directly. Using async/await will work fine as long as you ensure .emit(..) or .clients(..) isn’t called elsewhere before the Promise resolves. This is because .to(..) doesn’t return a new instance, and .emit(..) and .clients(..) reset Namespace.prototype.rooms, which is set by .to(..).

In your example:

[1, 2, 3].forEach(async () => {
  console.log("sending to private room");
  io.to('my-private-room').emit('event', {
    data: await sleep(100)
  });
});

The call order is:

io.to(roomId)   // rooms: []       -> [roomId]
io.to(roomId)   // rooms: [roomId] -> [roomId]
io.to(roomId)   // rooms: [roomId] -> [roomId]
io.emit(..)     // rooms: [roomId] -> []        Promise resolved
io.emit(..)     // rooms: []       -> []        Promise resolved
io.emit(..)     // rooms: []       -> []        Promise resolved

After the first Promise resolves, io.emit(..) sends to roomId and clears rooms. So subsequents emits send to every socket in the namespace.

The simple fix for this is to move the await before io.to(..) like such:

[1, 2, 3].forEach(async () => {
  console.log("sending to private room");
  let data = await sleep(100);
  io.to('my-private-room').emit('event', {
    data
  });
});

This yields a call order of:

io.to(roomId)   // rooms: []       -> [roomId]  Promise resolved
io.emit(..)     // rooms: [roomId] -> []
io.to(roomId)   // rooms: []       -> [roomId]  Promise resolved
io.emit(..)     // rooms: [roomId] -> []
io.to(roomId)   // rooms: []       -> [roomId]  Promise resolved
io.emit(..)     // rooms: [roomId] -> []

Hope this helps

5reactions
quangplcommented, May 22, 2019

Sometime my app has issue : Cannot read property 'emit' of undefined A part of my code :

    socket.on('typing', function (data) {
     
            if (data.to) {
                let strangerSocket1 = io.sockets.sockets[data.to];
                socket.emit('typing', data);
                strangerSocket1.emit('typing', data);
            }
    });

What can I do to resolve this problem ? Thank you !

Read more comments on GitHub >

github_iconTop Results From Across the Web

Client API - Socket.IO
An "error" event is emitted if verification fails. Verification happens at the connection level, before the HTTP request is sent.
Read more >
Socket emitting event multiple times - Stack Overflow
So in your loop you are saying send this everyone but the original socket, and you call that multiple times. What you want...
Read more >
Subscriptions in Apollo Server - Apollo GraphQL Docs
payload is the payload of the event that was published. variables is an object containing all arguments the client provided when initiating their...
Read more >
Everything you need to know about Socket.IO - Ably Realtime
While data can be sent in a number of forms, JSON is the simplest. ... are using io.emit() to send a message to...
Read more >
Redux Fundamentals, Part 6: Async Logic and Data Fetching
So far, all the data we've worked with has been directly inside of our React+Redux client application. However, most real applications need ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found