[英]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
循环中使用Promises
和async 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)
}
};
}
使用上述方式将帮助您遍历每个房间并逐个获取客户端。
虽然你不能强制它们同步运行
在 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))
和一个堆栈闪电战来玩这些:
我有一些简单而实用的东西,虽然它使用了我创建的库,所以它不是很地道。 没有什么好办法可以将异步代码变成同步代码,但是这样你就不用担心这种事情了。
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.