繁体   English   中英

如何使 socket.io 异步方法同步?

[英]How to make socket.io asynchronous method synchronous?

我在这里关注代码片段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'
});

让客户进入特定的房间。

有没有一种简单/惯用的方法可以使这个片段同步? 我想遍历一组房间并同步获取每个房间中的用户clients.length的计数(即在检索到当前房间的用户计数之前不要遍历循环。)

您可以在for循环中使用Promisesasync await

 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)
      }
   };
}

使用上述方式将帮助您遍历每个房间并逐个获取客户端。

虽然你不能强制它们同步运行

这里的其他答案似乎使事情过于复杂。 这可以在没有任何 3rd 方库的情况下轻松完成。

在 Promise 中包装回调可以让您更好地控制程序的流程。

// 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)
  )
})

如果您在异步 function 内部,则可以使用 await,这将使异步代码更像同步代码。 (尽管现代浏览器的模块中支持“顶级等待”)

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)
  }
}

使用 for-await 也是一种选择,如果您想在所有承诺解决之前开始迭代:

async function main() {
  const rooms = ['room1', 'room2', 'room3']
  for await (const clients of rooms.map(clientsInRoom)) {
    console.log(clients.length)
  }
}

你不能强制异步的东西在 js 中变得同步。 您可以按顺序执行异步操作(如果您愿意,也可以并行执行)。

回调:

function _getTotalClientsCb(rooms, count, cb) {
  if (rooms.length) {
    const room = rooms.shift()
    io.in(room).clients((err, clients) => {
      if (err) 
        return cb(err)
      count += clients.length;
      _getTotalClientsCb(rooms, count, cb)
    })
  } else {
    cb(null, count)
  }
}

function getTotalClientsCb(rooms, cb) {
  _getTotalClientsCb(rooms.slice(), 0, cb)
  // parallel execution
  // if (!rooms.length)
  //  cb(null, 0)
  // const allClients = [];
  // let count = 0
  // for (let room in rooms) {
  //   io.in(room).clients((err, clients) => {
  //     if (err)
  //       cb(err)
  //     allClients.push(clients)
  //     count += clients.length
  //     if (allClients.length === rooms.length) {
  //       cb(null, count)
  //     }
  //   })
  // }
}

getTotalClientsCb(rooms, (err, total) => console.log(total))

没有异步/等待的承诺:

function clientsPromise(room) {
  return new Promise((resolve, reject) => {
    io.in(room).clients((err, clients) => {
      if (err)
        reject(err)
      resolve(clients)
    })
  })
}

function getTotalClientsP(rooms) {
  return rooms.reduce((clientP, room) => 
    clientP.then(count => 
      clientsPromise(room).then(clients => 
        count += clients.length
      )
    )
  , Promise.resolve(0));
  // parallel execution
  // return Promise.all(rooms.map(room => clientsPromise(room))).then(
  //   allClients => allClients.reduce((count, clients) => count += clients.length, 0)
  // )
}

getTotalClientsP(rooms).then(total => console.log(total))

使用 async / await (基于@Shubham Katri 的回答)

function getTotalClientsAA(rooms) {
  let count = 0
  return new Promise(async (resolve, reject) => {
    for (let room in rooms) {
      try {
        const clients = await clientsPromise(room);
        count += clients.length
      } catch(err) {
        reject(err)
      }
    };
    resolve(count)
  })
}

getTotalClientsAA(rooms).then(total => console.log(total))

或者您可以在需要计数的 function 中使用基于 promise 的方法,通过将其声明为async (尽管这可能会在某些框架中导致意外问题):

async function myMainFucntion() {
   const rooms = ['1', '2', '2'];
   const totalClients = await getTotalClientsP(rooms); // or getTotalClientsAA(rooms)
   console.log(totalClients);
}

rxjs(外部库但非常惯用的 IMO):

import { bindNodeCallback, concat } from 'rxjs';
import { reduce } from 'rxjs/operators';

// for parallel
// import { forkJoin } from 'rxjs'
// import { map } from 'rxjs/operators';

function clients$(room) {
  return bindNodeCallback(io.in(room).clients)()
}

function getTotalClients$(rooms) {
  return concat(...rooms.map(room => clients$(room))).pipe(
    reduce((count, clients) => count += clients.length, 0)
  )
  // parallel execution
  // return forkJoin(rooms.map(room => clients$(room))).pipe(
  //   map(allClients => allClients.reduce((count, clients) => count += clients.length, 0))
  // )
}

getTotalClients$(rooms).subscribe(total => console.log(total))

和一个堆栈闪电战来玩这些:

https://stackblitz.com/edit/rxjs-xesqn9?file=index.ts

我有一些简单实用的东西,虽然它使用了我创建的库,所以它不是很地道。 没有什么好办法可以将异步代码变成同步代码,但是这样你就不用担心这种事情了。

const { pipe, map, reduce, get } = require('rubico')

const rooms = ['room1', 'room2', 'room3']

const getClientsInRoom = room => new Promise((resolve, reject) => {
  io.in(room).clients((err, clients) => {
    if (err) {
      reject(err);
    } else {
      resolve(clients);
    }
  })
});

const add = (a, b) => a + b

const getTotalClientsCount = pipe([
  map(getClientsInRoom), // [...rooms] => [[...clients], [...clients], ...]
  map(get('length')), // [[...clients], [...clients], ...] => [16, 1, 20, 0, ...]
  reduce(add, 0), // [16, 1, 20, 0, ...] => 0 + 16 + 1 + 20 + 0 + ...
]);

然后你会像这样在你的房间阵列上使用 function getTotalClientsCount

async function main() {
  const rooms = ['room1', 'room2', 'room3']
  const totalCount = await getTotalClientsCount(rooms)
  console.log(totalCount)
};
main();

如果您真的想变得花哨,您可以使用传感器来获取房间中的客户总数,而无需创建任何中间 arrays

const getTotalClientsCountWithTransducer = reduce(
  pipe([
    map(getClientsInRoom), // room => [...clients]
    map(get('length')), // [...clients] => 16
  ])(add), // 0 + 16 + ... ; add client count from room to total count, repeat for next room
  0,
);
async function main() {
  const rooms = ['room1', 'room2', 'room3']
  const totalCount = await getTotalClientsCountWithTransducer(rooms)
  console.log(totalCount)
};
main();

我在这里写了一个关于传感器的速成课程

这对我有用

const EventEmitter = require('events');

const myEmitter = new EventEmitter();

let rooms = ['room1', 'room2', 'room3']

rooms.forEach(room => {

    io.in(room).clients((err, clients) => {
      myEmitter.emit('cantUsers', room, clients.length); 
    });

  });

myEmitter.on('cantUsers', (room, cant) => {
  console.log(`In ${room} there are ${cant} users online`);
});

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM