3

I'm trying to implement the following scenario on my web page:

  1. A user visits a page (note that users have a unique ID)
  2. The user must perform a task and then press the next button
  3. When the button is pressed, I look up in a database to see if it was the user's first visit
  4. If it was he must wait 60 seconds before he can press the button so I alert him

I checked for solutions of similar synchronization problems:

but couldn't really adapt them to my case.

The following snippet gets executed every time the user presses the next button.

    // get timing for instruction reading
    var currentTime = new Date().getTime();
    totalTime = currentTime - startTime;
    console.debug("Time: " + totalTime);
    if (flag_instructions_pressed == false){
        $.ajax({
            url: "/IsNewUser/",
            type: "POST",
            contentType: "application/json",
            data: JSON.stringify({
                "_user_id": _user_id,
            }),
            dataType: "text",
            success: function( returned_data ) {
                _user_exp = parseInt(returned_data);
                flag_instructions_pressed = true;
            }
        });
    }

    if ( totalTime < 60000 && _user_exp == 0) {
        alert("You have to wait 60 seconds");
        return;
    }
  • The ajax request will return a string with the number of previous visits of the user in question, and it should be executed only once for this session (cause it adds the user to the database). The result is stored in the global variable _user_exp.

I have the following problem:

Even if a user has visited the page multiple times he will still be shown the alert since the if statement is executed before the success function from the ajax request.

How do I solve it?

NOTE 1: I cannot simply move the if statement into the success function because that should still be executed even after the flag_instructions_pressed is set to true. flag_instructions_pressed is just a flag initialized to false that gets set to true after the ajax request is performed. This prevents the request from happening again when the code is executed a second time.

NOTE 2: Obviously if I put an alert (or simply a timeout) before the if statement, everything works fine. But I think it is bad programming practice and would like to learn how to deal with this type of synchronization problems.

Community
  • 1
  • 1
Matteo
  • 7,924
  • 24
  • 84
  • 129
  • To be clear: you *only* want to display the alert *upon* receiving the AJAX message? And what do you mean by `.. because that should still be executed even after the flag_instructions_pressed is set to true.` – Jean-Paul Mar 29 '15 at 18:25
  • It seems like you could have simplified your question a ton. It's mainly theoretical. – lawx Mar 29 '15 at 18:26
  • What does `flag_instructions_pressed` do? It seems to be central to your question, but you haven't told us what it does. – JLRishe Mar 29 '15 at 18:27
  • @Jean-Paul 1) Yes. The alert should be shown after the `ajax` response. Because the response will tell me if the user was a returning one (in which case the `if` statement will be skipped -`_user_exp` will result `>0`-) or a new one for which `_user_exp == 0`. 2) after the code `returns` the user might press the `next` button again, and the `if` statement should be checked again (however this time the `_user_exp` variable is stored and no need for `ajax` request) – Matteo Mar 29 '15 at 18:29
  • @JLRishe - `flag_instructions_pressed` is just a flag initialized to `false` that gets set to `true` after the `ajax` request is performed. This prevents the request from happening again when the code is executed a second time – Matteo Mar 29 '15 at 18:31

2 Answers2

1

One solution to this is to make use of promises. They serve as a placeholder for a future value and you can "check their future value" multiple times:

var newUserCheck;

function nextButtonHandler() {
    // get timing for instruction reading
    var currentTime = new Date().getTime();
    totalTime = currentTime - startTime;
    console.debug("Time: " + totalTime);

    if (!newUserCheck) {
        newUserCheck = $.ajax({
            url: "/IsNewUser/",
            type: "POST",
            contentType: "application/json",
            data: JSON.stringify({
                "_user_id": _user_id,
            }),
            dataType: "text"
        }).then(function ( returned_data ) {
           _user_exp = parseInt(returned_data);
           return _user_exp;
        });
    }

    newUserCheck.then(function (user_exp) {
        if ( totalTime < 60000 && user_exp === 0 ) {
            alert("You have to wait 60 seconds");
            return;
        }

        // otherwise, complete the "next button procedure" here
    });
}
JLRishe
  • 99,490
  • 19
  • 131
  • 169
  • Thanks for your answer! So basically `newUserCheck` substitutes my `flag_instructions_pressed` and the `then` function will guarantee that that code is executed only if the `newUserCheck` has been set? Is the `newUserCheck` set only after the end of the `success` function? – Matteo Mar 29 '15 at 18:41
  • `newUserCheck` is a promise, and it is assigned to the variable `newUserCheck` immediately, but the `then`s attached to it will only be executed once the AJAX call completes. So yes, this will ensure that the AJAX call only takes place once and that the ensuing operations will only happen once the result comes back. – JLRishe Mar 29 '15 at 18:51
  • For some reason things are not working. I tried printing `console.debug(' $> In function : ' + _user_exp);` in the `function(_user_exp)` just above the `if` statement and I am getting `undefined` as an answer. Any idea? There is a typ0 in your answer but I corrected it before posting on my web page. Thanks! – Matteo Mar 29 '15 at 19:15
  • @Matteo Where is the typ0? – JLRishe Mar 29 '15 at 19:17
  • `user_exp` instead of `_user_exp`, but I corrected that in my code – Matteo Mar 29 '15 at 19:18
  • @Matteo That was intentional. Could you post the code you're trying in PasteBin so that I can see it? – JLRishe Mar 29 '15 at 19:19
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/74053/discussion-between-matteo-and-jlrishe). – Matteo Mar 29 '15 at 19:22
1

How about turning the if statement into a function:

function testTime() { 
  if ( totalTime < 60000 && _user_exp == 0) {
    alert("You have to wait 60 seconds");
    return;
  }
}

And then make the other if statement into an if/else, calling this function both in the else block (flag_instructions_pressed == true) and in the success function of the ajax call (flag_instructions_pressed == false):

if (flag_instructions_pressed == false){
    $.ajax({
        url: "/IsNewUser/",
        type: "POST",
        contentType: "application/json",
        data: JSON.stringify({
            "_user_id": _user_id,
        }),
        dataType: "text",
        success: function( returned_data ) {
            _user_exp = parseInt(returned_data);
            flag_instructions_pressed = true;
            testTime();
        }
    });
} else {
  testTime();
}
badjuju
  • 11
  • 2
  • Thanks for the answer. However, I was hoping in a solution that would avoid me having many nested statements, but it seems like it might work. – Matteo Mar 29 '15 at 18:42
  • Ah, then you could look into promises as JLRishe suggests. Keep in mind that if you are needing to support IE (especially older versions), you will need a polyfill as it won't have this functionality out of the box. – badjuju Mar 29 '15 at 19:07