0

I'm new to JavaScript, and still not familiar with asynchronous functions...

I'm working with node.js to create a chat server, this is a part of my code that's listen to getListConnected event and when it triggered it look for all connected clients in a given namespace, and then for each client I store their 'username' to another array, convert it to JSON response and then send:

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

The problem with this code is the client.get() method is asynchronous which means that usernames are sent empty.

How can I wait for usernames until it's filled, or how can I wait for the loop until it's finished?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Marwen Trabelsi
  • 4,167
  • 8
  • 39
  • 80

2 Answers2

1

You could use middleware for promises, something like Bluebird, or you could keep a counter to check if all usernames have been gotten, something like

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

        });
    }

});
adeneo
  • 312,895
  • 29
  • 395
  • 388
  • thx, i'll i'm browsing Bluebird, but in your code is not ok, this can send array more than once whenever c1=c2... but it's ok to have different ideas! thanks again! – Marwen Trabelsi Jul 29 '14 at 01:30
  • But when would c1 be the same as c2, other than when every username is gotten. The loop finishes right away counting up the first counter, the async function finishes some time in the future when the first counter is incremented, then incrementing the second counter. The first counter will always be one step ahead of the async counter, so the only time they should be the same is at the end. – adeneo Jul 29 '14 at 01:38
  • 1
    However I would probably go for Bluebird, Q or async.js myself. – adeneo Jul 29 '14 at 01:40
1

Here's a Bluebird example, customised for your situation: [it borrows heavily from Victor Quinn's Example]

Using the same Promise definition that Victor used:

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

Instantiated with your custom call:

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);
});
Caspar Harmer
  • 8,097
  • 2
  • 42
  • 39