Trax
Trax

Reputation: 1538

Variable not updating properly

This is the relevant code :

This sends a request to the server, takes an userName as an argument.

function send_CLIENT_LOOKUP(userName)
{
    send(
        auth.AOCP.CLIENT_LOOKUP,
        [ [ 'S', userName.toString() ]
        ]
    )

}

This part handles the response from the server.

handle[auth.AOCP.CLIENT_LOOKUP] = function (data, u)
        {
        console.log('CLIENT_LOOKUP')
        var userId = u.I()
        var userName = u.S()
        u.done()
        console.log({ userId : userId, userName : userName })   // this works properly
        var idResult = userId; // I can actually use the userID but I just assigned it to idResult to make the code easier to understand.
        }

The above 2 functions work how they are supposed to, nothing to change/fix there.

Now I have a function that receives a request from one user and sends a request to another user, it takes 2 arguments: first arg is the userId of the user that sends the request and the second is the userName of user the request will be sent to however the server/game only works with userIds so the userName has to be converted:

var commands = {
    invite: function(userId, userName) {
        send_CLIENT_LOOKUP(userName); // send a request to the server to find out the userId of the userName
        send_PRIVGRP_INVITE(idResult);

    }

}

The problem is that idResult == undefined, unless I call cmd.invite() again then idResult == 'with the previous response like this:

cmd.invite(user1);
cmd.invite(user2);
cmd.invite(user3);

And the output of idResult is:

idResult == undefined
idResult == 'info for user1'
idResult == 'info for user2'

I tried defining idResult outside response handler and update it inside, to make sure its not some sort of delay from the server I just did a massive invite spam and the result was the same, 1 step behind no matter how fast I sent the invites. Any suggestions are welcome :)

Upvotes: 0

Views: 52

Answers (2)

nponeccop
nponeccop

Reputation: 13677

The problem is rather hard to solve, as sending and collecting replies are isolated. It seems that send() sends a packet and handle() receives a reply, and there can be many possible unrelated packets between sending and receiving.

If it's the case then a special global map should be created to link requests and responses. For cases when there are 2 concurrent requests for the same username, EventEmitter from events module is a good fit, as it's essentially a multimap of strings to callbacks:

var events = require('events')

var outstandingLookups = new events.EventEmitter()

So when you send a lookup you register a one time listener, so there's no need for manual cleanup:

var commands = {
    invite: function(userName) {
        outstandingLookups.once(userName, function (idResult)
        {
            send_PRIVGRP_INVITE(idResult);
        })
        // send a request to the server to find out the userId of the userName
        send_CLIENT_LOOKUP(userName); 
    }   
}

In response handler emit conveniently calls for you all the callbacks expecting userId for the received userName:

handle[auth.AOCP.CLIENT_LOOKUP] = function (data, u)
{
        console.log('CLIENT_LOOKUP')
        var userId = u.I()
        var userName = u.S()
        u.done()
        console.log({ userId : userId, userName : userName })   // this works properly
        // I can actually use the userID but I just assigned it to idResult
        // to make the code easier to understand.
        var idResult = userId; 

        // this is the only addition to original handler code
        outstandingLookups.emit(userName, idResult)
}

The need to use outstandingLookups.once is actually an implementation detail and it should be properly encapsulated/hidden if you want to lookup userIds in more than one place in the code.

I will use promises from q npm library as I remember the interface, but similar ES6 promises should be used in production code as they are a modern standard.

var Q = require('q')

var commands = {
    lookupUserName: function (userName)
    {
        var result = Q.defer()
        outstandingLookups.once(userName, function (idResult)
        {
            result.resolve(idResult);
        })
        // send a request to the server to find out the userId of the userName
        send_CLIENT_LOOKUP(userName); 

        return result.promise 
    },

    invite: function(userName) {
        commands.lookupUserName(userName).then(function (idResult)
        {
            send_PRIVGRP_INVITE(idResult);
        })
    }   
}

The code for commands.invite is much cleaner now.

Upvotes: 1

Jeff
Jeff

Reputation: 6953

It looks like you're doing an async-request but updating the var idResult synchronously.

So you should put the call to send_PRIVGRP_INVITE(idResult) into the callback or response handler of send_CLIENT_LOOKUP(userName)

Upvotes: 0

Related Questions