-1

I have a working ajax function that when called will display the current time, and then setTimeout for ten seconds before displaying the new time. I call this function when onkeyup is triggered on an text input, and it works. But there is a slight problem. If I type something else in the text input after the ajax function has already been called, it'll call another ajax function, and have two ajax functions running at the same time. For example:

If the first ajax function was called at 3:00:00 when it was triggered, and the second ajax function is called at 3:00:05, that means that there are now two ajax functions running at the same time. The first ajax function will be triggered again at 3:00:10, after the 10 second setTimeout, and the second ajax function will be triggered again at 3:00:15, after its 10 second setTimeout. So the more times you trigger the onkeyup in the text input, the more times the function will be called. I just want 1 function of itself to be running at the same time, not 2, 3, or more. How do I do that? Thanks.

ajax.php

<script type = "text/javascript"> 

function timeoutAjax(url,type,theName,id,timeout) {

      $.ajax({
           type: "POST",
           url: url,
           data: { select: $(type+'[name='+theName+']').val()},
           error: function(xhr,status,error){alert(error);},
           success:function(data) {
             document.getElementById( id ).innerHTML = data;
             setTimeout(function() { timeoutAjax(url,type,theName,id,timeout); }, timeout);
           }

      });

}

</script>

test1.php

<?php

include('ajax.php');

echo "<input type = 'text' name = 'name' onkeyup = 'timeoutAjax(\"test2.php\",\"input\",\"name\",\"output1\",\"10000\")'>";
echo "<div id = 'output1'/>";

?>

test2.php

<?php

$time = date('H:i:s A');
echo $time;

?>

************MORE DETAILS************

echo "<input type = 'submit' name = 'name1' value = 'Reset' onclick = 'timeoutAjax(\"test2.php\",\"input\",\"name1\",\"output1\",\"10000\")'>";
echo "<input type = 'submit' name = 'name2' value = 'Reset' onclick = 'timeoutAjax(\"test2.php\",\"input\",\"name2\",\"output2\",\"10000\")'>";

echo "<div id = 'output1'/>";
echo "<div id = 'output2'/>";
frosty
  • 2,559
  • 8
  • 37
  • 73

2 Answers2

2

If I understand your question correctly, you actually trying to achieve two things here:

1. Only the last ajax call

After the last key stroke, do some ajax call. Any ajax call that is already busy can be skipped, you are just interested in the last one.

This should not be to hard. jQuery returns an xhr object when you call the ajax function. On that xhr object, you can just call the abort() method to cancel a pending call. (Abort Ajax requests using jQuery)

2. Repeat the ajax call every x time

Right now you set a timeout in your ajax success function that will just repeat the call after a given time. Problem is that when you call your ajax function again from the outside (so not recursively I mean, but by another keystroke or something) you will just create another infinite string of ajax calls. After a while you'll end up with a huge queue of calls that will start to overlap and eat up all your resources.

This can easily be solved by storing the result of that setTimeout in a variable, and calling clearTimeout on that variable each time before you set a new timeout. This way you cancel the old 'queue' and just start a new one.

So enough poor english, let's try to show what I mean by updating your code:

function timeoutAjax(url, type, theName, id, timeout, trigger) {
    // abort pending calls
    if (trigger.xhr) {
        trigger.xhr.abort();
    }
    // abort queued calls
    clearTimeout(trigger.queued);

    // make the call
    trigger.xhr = $.ajax({
        type: "POST",
        url: url,
        data: {
            select: $(type + '[name=' + theName + ']').val()
        },
        error: function (xhr, status, error) {
            alert(error);
        },
        success: function (data) {
            document.getElementById(id).innerHTML = data;
            // queue a new call
            trigger.queued = setTimeout(function () {
                timeoutAjax(url, type, theName, id, timeout, trigger);
            }, timeout);
        }

    });

}

Just one more sidenote. Even with the changes I made to your code, you will be aborting and making a new ajax call on every key stroke. And aborting an ajax call does not automatically mean your server stops handling the request, depnding on what backend you are using. For the simple php script you are using now it is probably fine, but still, it is probably better to wait until the user is done with typing before you make your first call. This is called Debouncing and isn't very hard to implement either: http://davidwalsh.name/javascript-debounce-function

Community
  • 1
  • 1
Pevara
  • 14,242
  • 1
  • 34
  • 47
  • Can I have it not say abort when it aborts? – frosty Aug 04 '15 at 21:37
  • This is the best answer – dlporter98 Aug 04 '15 at 21:47
  • that is your error alert at work. Aborting triggers the error callback. Just check in there wether it is an abort or a genuine error, and only alert real errors. – Pevara Aug 04 '15 at 21:47
  • Okay. This works. There's just one more issue. If there are more than 1 timeout ajax function being called at once on that page, it stops working. For example, what if you want two buttons that calls this ajax function? Then it stops working. Please take a look at my updated code above, below the heading "MORE DETAILS". Thanks. – frosty Aug 04 '15 at 21:52
  • Well, that's what you get when using global variables... I was going to make a remark about it, but I thought my answer was already elaborate enough. Anyway, you could just supply the trigger (button, input, whatever element that triggers the function) as an additional attribute, and attach the xhr and timeout variables to that. Have a look at the updated code. That would look something like this: `onclick = 'timeoutAjax(\"test2.php\",\"input\",\"name2\",\"output2\",\"10000\", this)` – Pevara Aug 04 '15 at 22:00
  • The updated code only calls the function when you click the button. It doesn't call the function after the timeout. – frosty Aug 04 '15 at 22:24
0

Create a status variable that tracks if the ajax call is running. Set it to false initially. Then when the function is called, check the status; if not running execute the ajax call, set the status var to true, then set it to false again in the success callback:

<script type = "text/javascript">
    //Create the status var (This may not be the best place to initialize it). Use your best judgement.
    var running = false;

    function timeoutAjax(url,type,theName,id,timeout) {
        //Check if there is an ajax call in progress. If not execute one.
        if(!running)
        {
            //Change the status to running
            running = true;
            console.log('In if')
            console.log(running)
            $.ajax({
                type: "POST",
                url: url,
                data: { select: $(type+'[name='+theName+']').val()},
                error: function(xhr,status,error){alert(error);},
                success:function(data) {
                    document.getElementById( id ).innerHTML = data;
                    setTimeout(function() {
                        
                        //Set the status to false for the inner function call
                        running = false;
                        timeoutAjax(url,type,theName,id,timeout);
                        //Set the status to true for the outer function call
                        runnng = true;
                    }, timeout);
                }

            });
        }
    }
</script>

The outer function which triggers the first ajax call and the timeout will only be run once,; however, the inner function will run every ten seconds continuously.

General Grievance
  • 4,555
  • 31
  • 31
  • 45
dlporter98
  • 1,590
  • 1
  • 12
  • 18
  • Doesn't work. It still runs multiple copies of itself at the same time if triggered more than once. – frosty Aug 04 '15 at 20:41
  • use console.log to print all the running variables. This should work. – dlporter98 Aug 04 '15 at 20:52
  • Run my codes from above with your new codes. See if it actually resets or not. – frosty Aug 04 '15 at 20:53
  • And if it does, I'll definitely accept and upvote this. – frosty Aug 04 '15 at 20:55
  • I changed the code after testing it on my server. I added the status variable inside the timeout callback. this seems to do the trick. – dlporter98 Aug 04 '15 at 21:13
  • Try testing again with my new changes. – dlporter98 Aug 04 '15 at 21:17
  • It works. But not the way I intended. When the new ajax function is called, I want it to terminate the old ajax function, and start the timeout from the new ajax function. For example, if the old ajax function was called at 3:00:00, and the new ajax function was called at 3:00:05, I want to terminate the old ajax function called at 3:00:00, and start the timeout at 3:00:05, so the next called of the function would be at 3:00:15 instead of 3:00:10. – frosty Aug 04 '15 at 21:34