我在这里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
(即不要在循环中进行迭代,直到检索到当前房间的用户数为止)。
答案 0 :(得分:1)
您可以在Promises
循环内使用async await
和for
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)
}
};
}
使用上述方式将帮助您遍历每个房间并按顺序逐个吸引客户。
尽管不能强迫它们同步运行
答案 1 :(得分:1)
在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)
)
})
如果您位于异步函数内部,则可以使用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)
}
}
如果要在所有promise都未解决之前开始进行迭代,则也可以使用“等待”:
async function main() {
const rooms = ['room1', 'room2', 'room3']
for await (const clients of rooms.map(clientsInRoom)) {
console.log(clients.length)
}
}
答案 2 :(得分:0)
您不能强迫异步事物在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))
或者您可以通过声明async
来使用需要计数的基于函数的基于promise的方法(尽管这可能在某些框架中引起意外的问题):
async function myMainFucntion() {
const rooms = ['1', '2', '2'];
const totalClients = await getTotalClientsP(rooms); // or getTotalClientsAA(rooms)
console.log(totalClients);
}
rxjs(外部lib,但非常惯用的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))
和一堆堆炸弹可以玩这些东西:
答案 3 :(得分:0)
我有一些简单和功能,尽管它使用了我创建的库,所以它不是很惯用。没有将异步代码转换为同步代码的好方法,但是这种方式不必担心这种事情。
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 + ...
]);
然后您将像这样在房间阵列上使用功能getTotalClientsCount
async function main() {
const rooms = ['room1', 'room2', 'room3']
const totalCount = await getTotalClientsCount(rooms)
console.log(totalCount)
};
main();
如果您真的想花哨的话,可以使用传感器来获取房间中客户的总数,而无需创建任何中间阵列
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();
我在换能器here上写了一个速成课程
答案 4 :(得分:-1)
这对我有用
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`);
});