views:

36

answers:

2

I have a function from which I would like to return a value as a series of events whenever a button is clicked. However, I can't figure out how to retrieve the value from onreadystatechange. How can I make it so I can return vicArray[vicID]?

function selectVictim()
{
var vicString;
var vicArray;
var vicID;

var params = "url=queenofsheep.com/Sheep/victims.php";
var request = new ajaxRequest();

request.open("POST", "victims.php", true);
request.setRequestHeader("Content-Type",
                             "application/x-www-form-urlencoded");
request.setRequestHeader("Content-Length", params.length);
request.setRequestHeader("Connection", "close");

request.send(params);

request.onreadystatechange = function ()
{
    if (this.readyState == 4)
    {
        if (this.status == 200)
        {
            if (this.responseText != null )
            {
                vicString = this.responseText;
                vicArray = JSON.parse(vicString);
                vicID = Math.floor(Math.random() * (vicArray.length - 1));
            }
            else alert("Ajax error: No data received");
        }
        else alert("Ajax Error: " + this.statusText);
    }
}
alert(vicArray[vicID]);
}
A: 

You can't. Your onreadystatechange handler gets called long after the selectVictim function returns.

What you have here is, so to say, "asynchronous functions" - i.e. functions that generate their return value not immediately, but after a certain asynchronous process.

To deal with this, one has to use a callback to provide return value:

function selectVictim( callback )
{
    ...
    ...

    request.onreadystatechange = function() {
        ...
        vicArray[vicID] = ...;
        callback( vicArray[vicID] );
    }
}

Note the callback argument. Whoever calls this function, must provide another function for this argument. Then note how selectVictim calls the callback when the return value is ready.

Now wherever you call selectVictim, you must modify your code from:

function someFunc()
{
    doSomething();

    var vic = selectVictim();

    domeSomethingElseWithVictim( vic );
}

To:

function someFunc()
{
    doSomething();

    selectVictim( function( vic ) {

        domeSomethingElseWithVictim( vic );

    } );
}

Simple enough?

Fyodor Soikin
So what is another way to go about this?
Andrew
Mmmm... Another way? I don't quite get the question. What another way?
Fyodor Soikin
Awesome, thanks!
Andrew
A: 

I would use a callback, in which you can pass the function that runs once the response is ready.

Add callback as a param:

function selectVictim(callback)

and then:

vicString = this.responseText;
vicArray = JSON.parse(vicString);
vicID = Math.floor(Math.random() * (vicArray.length - 1));
if (callback) {
 callback(vicID);
}

When you call the function you could do this:

selectVictim(function(vicID){ 
  console.log('This is the id: ', vicID); 
});

You can also pass the function as a value if you prefer not to do it directly. Note: it gets a little tricky if you make a ton of requests and you need to preserve the order in which they were made, but there are ways to handle that.

michael