1

I realise this question was already asked on StackOverflow but the answer is mostly focused on jQuery.

How do I return the response from an asynchronous call?

I have a JavaScript function that allows people on the website to vote. However they can only vote once a day. So I am trying to call another JS function which is doing an AJAX call. This in turn calls a PHP page, which is doing a query in the database to check whether the caller voted in the last 24H.

function can_vote(user_id)
{
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.open("GET", "rating.php?&user_id=" + user_id, true);
    xmlhttp.send();
    var result = true;
    xmlhttp.onreadystatechange=function()
    {
        if (xmlhttp.readyState == 4 && xmlhttp.status == 200)
        {
            console.log("answer: " + xmlhttp.responseText);

            return (xmlhttp.responseText == "false") ? false : true; // doesn't work
        }
    }
}

function rating(user_id)
{
    if (can_vote(user_id) == false)
    {
        // display error message 
        ...
        return false;
    }
    ...
}

This doesn't work for the reasons explained in the link I provided earlier. I tried to follow the suggestions suggested in this link but I can't make it work (I can only use JavaScript no jQuery). I tried to implement a callback system:

can_vote(callback, user_id)
{
    ...
    xmlhttp.onreadystatechange=function()
    {
        // if we can a positive answer from the rating.php
        // then call the callback function
        callback();
    }
}

function rating(user_id)
{
    var result = false;
    can_vote(function() {
        result = true;
    }, user_id);
    console.log(result); // always false
    ...
}

But while the mechanism works, it doesn't update the variable true, which seems to be "local" to the callback function.

It feels like I am close to the solution, but can't go any further. Passing a variable by reference to the function doesn't work in JS, so I have explored all options I can think of.

Could someone please help and suggest a fix/solution (using JavaScript only)?

EDIT 1:

As suggested earlier, while the answer in How do I return the response from an asynchronous call? is very thorough and informative, it answers the question using jQuery not JavaScript.

EDIT 2 and ANSWER:

I can't answer my own question for whatever reason, but fair enough, because in fact I should have thought twice before asking it. So thanks for all answers.

  • Apologies, it is indeed a duplicate of the question linked above
  • Egg Vans suggested to rethink the problem. This is indeed one way. In fact it's possible in the PHP page creating the rating system, to actually call a PHP function when the user votes. My system requires the user to click on "stars" to vote. Thus, I could set the onclick callback to a PHP function rather than a JS function and bypass AJAX all together.
  • however the reason I can't do this is because finding out the number of stars chosen by the user requires some calls to DOM functions.

Now, while I consider myself as an okay programmer, the problem is that in this particular situation, I completely overlooked the code. The original question contains the correct answer for "processing" the result of an AJAX call from within a JS function. So what I have overlooked is that in fact, the actual result of the AJAX call will be "processed" in the callback function. So for example in my case if the user can vote, then I should execute some code within this call back function, and if he/she can't vote, I should execute some other code in the same call back function. In other words:

function can_vote(callback, user_id)
{
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.open("GET", "rating.php?user=" + user_id, false);
    xmlhttp.send();
    xmlhttp.onreadystatechange=function()
    {
        if (xmlhttp.readyState == 4 && xmlhttp.status == 200)
        {
             // pass the result to the callback function and process this result accordingly
             callback(xmlhttpx.responseText);
        }
    }
}

function star_rating(user_id)
{
    ...

    can_vote(function(result)
    {
        if (result == false)
        {
            // you can't vote display error message
        }
        else
        {
            // you can vote do your stuff here, find out what's the rating from the user
            // add it to the db, and update the current rate yet using another
            // AJAX call.
        }
    }, user_id);

    // any code you put after this line will be processed likely before the AJAX call returns
    ...
}

Thanks again everything for your patience. Hope though that this concrete example will help others.

Community
  • 1
  • 1
user18490
  • 3,546
  • 4
  • 33
  • 52
  • I think you need to rethink how your trying to do this and have the voting capability disabled and only enable it inside the callback. – Egg Vans Aug 22 '14 at 12:59
  • 2
    Please read the whole question, not only the accepted answer. There are answers that ([even explicitly](http://stackoverflow.com/a/16825593/1048572)) don't focus on jQuery as well. And you can use deferreds/promises without jQuery (it might even be better) with some custom library. – Bergi Aug 22 '14 at 12:59
  • I forgot about Promises. They're still kinda new so I haven't played with them. [More on Promises](http://www.html5rocks.com/en/tutorials/es6/promises/) – Machavity Aug 22 '14 at 13:04
  • @Bergi. I read the whole thread again and yes I see the answer in the original post, however I think it requires some changes to the way the original JS caller function is written. While my question is indeed a duplicate, I think an answer to this question might provide a real and complete example to how this should/could work. I will actually post the code which is a solution to the problem I face. – user18490 Aug 22 '14 at 13:13
  • Yes, the solution will always require a change to the caller function, as by using an async function it becomes async itself. – Bergi Aug 22 '14 at 13:20

2 Answers2

2

The A in AJAX stands for asynchronous. That means that your JS is not going to wait for the AJAX to finish before moving on to the next thing (in other words it doesn't "block" the script).

This is why you have a callback function that executes when the call is complete. The callback is triggered only when you have the data you need.

Machavity
  • 30,841
  • 27
  • 92
  • 100
  • Yes this is very well explained in the post I added the link to. I got the concept of asynchronous, but the answer in the other post suggests this can work if you create a dependency on the variable result. However, the answer uses jQuery and I can't make it work in pure JS. The question for me is more is there an actual solution to this problem with JS and JS only. Thanks. – user18490 Aug 22 '14 at 12:56
  • The only way I know of to make it do that is to make your request synchronous. This is generally a **bad thing** (because it now blocks ALL JS, not just your function), and has been deprecated, but [here's how to do a synchronous call](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Synchronous_and_Asynchronous_Requests). – Machavity Aug 22 '14 at 13:01
0

I believe you should move console.log inside of your callback function:

 function rating(user_id) {
     var result = false;
     can_vote(function(result) {
         result = true;
         console.log(result); 
     }, user_id); }

You always have "false" result because you are using asynchronous callback.

UPD:

Maybe i didn't catch your problem. If you need to process the result of callback just call your processing method from callback function body:

     can_vote(function(result) {
        processCanVoteResult(result); //call result processing
     }, user_id); }
Greenonion
  • 87
  • 5
  • yes that would work. But then at this point I would need to EXIT the rating function. How can I do that? since if I return false, I am only in the callback function at this point. So it won't leave the rating function, which is what I need. – user18490 Aug 22 '14 at 12:58
  • @user18490, you are "exited" of rating function. By the time your callback function is invoked the code in your rating function will have already run and returned to whatever called it – Patrick Evans Aug 22 '14 at 13:01
  • @Patrck: precisely, I don't want the rest of the code to be executed before I get the callback function called. I know what to do now, I will post the code soon. But your answer was helpful. – user18490 Aug 22 '14 at 13:16