Como posso esperar por eventos assíncronos em Javascript?
-
20-12-2019 - |
Pergunta
Sou novo em JavaScript e ainda não estou familiarizado com funções assíncronas...
Estou trabalhando com node.js para criar um servidor de chat, isso é uma parte do meu código que está escutando getListConnected
evento e quando foi acionado procure por todos conectados clients
em um determinado namespace e, para cada cliente, armazeno seu 'nome de usuário' em outro array, converto-o em resposta json e envio:
socket.on('getListConnected', function(msg){
var clients = namespace.clients();//get all client in that nsp
var usernames = Array() ;//init array
for(i in clients)//for each clients
{
clients[i].get("username", function(value){
usernames.push(value);
});
}
socket.emit('getListConnected',JSON.stringify(usernames));//send
});
O problema com este código é que o método client.get() é assíncrono, o que significa que os nomes de usuário são enviados vazios.
Como posso esperar pelos nomes de usuário até que sejam preenchidos ou como posso esperar pelo loop até que termine?
Solução
Você poderia usar middleware para promessas, algo como pássaro azul, ou você pode manter um contador para verificar se todos os nomes de usuário foram obtidos, algo como
socket.on('getListConnected', function (msg) {
var clients = namespace.clients();
var usernames = [];
var counter1 = 0;
var counter2 = 0;
for (i in clients) {
counter1++; // number of clients
clients[i].get("username", function (value) {
usernames.push(value);
counter2++; // number of clients added to array
if (counter1 === counter2) {
socket.emit('getListConnected', JSON.stringify(usernames));
}
});
}
});
Outras dicas
Aqui está um exemplo do Bluebird, personalizado para sua situação:[ele toma emprestado pesadamente de Exemplo de Victor Quinn]
Usando a mesma definição de Promessa que Victor usou:
var Promise = require('bluebird');
var promiseWhile = function(condition, action) {
var resolver = Promise.defer();
var loop = function() {
if (!condition()) return resolver.resolve();
return Promise.cast(action())
.then(loop)
.catch(resolver.reject);
};
process.nextTick(loop);
return resolver.promise;
};
Instanciado com sua chamada personalizada:
var i = 0;
promiseWhile(function() {
return i < clients.length;
}, function(value) {
return new Promise(function(resolve, reject) {
clients[i].get("username", function(value){
usernames.push(value);
});
i++;
resolve();
});
}).then(function() {
console.log("usernames are: " + usernames);
});