Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make socket.io asynchronous method synchronous?

I was following the code snippet here https://github.com/socketio/socket.io-redis#redisadapterclientsroomsarray-fnfunction

io.in('room3').clients((err, clients) => {
  console.log(clients); // an array containing socket ids in 'room3'
});

to get the clients in a particular room.

Is there a simple/idiomatic way I can make this snippet synchronous? I want to loop over an array of rooms and synchronously get the count of users clients.length in each room (ie don't iterate over the loop until the user count of the current room has been retrieved.)

like image 256
user784637 Avatar asked Jul 25 '26 20:07

user784637


2 Answers

You can make use of Promises and async await within a for loop

 async function getClients() {
    for(let room in rooms) {
      try{
       const promise = new Promise((res, rej) => {
           io.in(room).clients((err, clients) => {
              if(err) {
                 rej(err);
              } else {
                  res(clients); // an array containing socket ids in room
              }
           });
       })
       const clients = await promise;
       console.log(clients);
      }catch(err) {
        console.log(err)
      }
   };
}

Using the above manner will help you iterate over each room and get the clients sequentially one by one.

Although you can't force them to run synchronously

like image 77
Shubham Khatri Avatar answered Jul 27 '26 09:07

Shubham Khatri


The other answers here seem to overcomplicate things. This can easily be done without any 3rd party libraries.

Wrapping a callback in a Promise gives you more control over the flow of the program.

// The purpose of this is just to convert a callback to a Promise:
// clientsInRoom('room1') is a promise that resolves to `clients` for that room.
const clientsInRoom = room => new Promise((resolve, reject) => {
  io.in(room).clients((err, clients) => 
    err ? reject(err) : resolve(clients)
  )
})

If you are inside of an async function, you can use await, which will make asynchronous code feel more like synchronous code. (although "top-level await" is supported within modules in modern browsers)

async function main() {
  const rooms = ['room1', 'room2', 'room3']

  // If either one of the promises reject, the statement will reject
  // Alternatively, you can use Promise.allSettled()
  const allClients = await Promise.all(rooms.map(clientsInRoom))

  // You can use map to turn this into an array of the lengths:
  const lengths = allClients.map(clients => clients.length)

  // Alternatively, if you want the feel of a synchronous loop:
  for (const clients of allClients) {
    console.log(clients.length)
  }
}

Using for-await is also an option, if you want to start iterating before all promises have resolved:

async function main() {
  const rooms = ['room1', 'room2', 'room3']
  for await (const clients of rooms.map(clientsInRoom)) {
    console.log(clients.length)
  }
}
like image 37
Tobias Bergkvist Avatar answered Jul 27 '26 09:07

Tobias Bergkvist



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!