2

I am running an AJAX request when the user types in an input field and then displaying the result on the page. When the user presses the backspace to delete all of what they've inputted, I use .empty to remove the result from the page.

However, if you press the backspaces really quickly, the result is removed from the page, but then because the last AJAX query hasn't last executed, the result from that query appears!!!

I have looked at Abort Ajax requests using jQuery but that didn't help, and have tried adding return: false; after $("#results").empty(); to no avail.

If there are any remaining AJAX calls when if(this.value.length < 1) { is true, I would like to abort them all inside that function.

$("input#enter").keyup(function() {
    if(this.value.length < 1) {
        $("#display").empty();
    }else{ 
        $.ajax({
            type: "POST",
            url: "getdata.php",
            data: "title=" + this.value,
            success: function(data) {
                $("#display").empty(); 
                $("#display").html(data);
            }
        });
    } 
});
The Codesee
  • 3,714
  • 5
  • 38
  • 78

2 Answers2

1

You can use $.active to check if $.ajax() call is active before calling next $.ajax()

$("input#enter").keyup(function() {
    if(this.value.length < 1) {
        $("#display").empty();
    }else{ 
      if (!$.active) {
        $.ajax({
            type: "POST",
            url: "getdata.php",
            data: "title=" + this.value,
            success: function(data) {
                $("#display").empty(); 
                $("#display").html(data);
            }
        });
      }
    } 
});

You can also include attaching .ajaxComplete() to document to call next $.ajax() call when current call completes

function request(value) {
  return  $.ajax({
            type: "POST",
            url: "getdata.php",
            data: "title=" + value,
            success: function(data) {
                $("#display").empty(); 
                $("#display").html(data);
            }
        });
}

$("input#enter").keyup(function() {
    if(this.value.length < 1) {
        $("#display").empty();
    }else{ 
      if (!$.active) {
        request(this.value)
      } else {
        $(document).one("ajaxComplete", function() {
          request(this.value)
        })
      }
    } 
});

One approach to abort requests is to use XMLHttpRequest(), push requests to an array, then call .abort() on each element of the array

function request(data) {
  let fd = new FormData();
  fd.append("html", data);
  fd.append("delay", Math.floor(Math.random() * 10));
  let xhr = new XMLHttpRequest();
  xhr.open("POST", "/echo/html/", true);
  xhr.onload = function() {
    console.log(xhr.responseText);
  }
  xhr.onabort = function() {
    console.log("request " + requests.indexOf(xhr) + " aborted")
  }
  xhr.send(fd);
  return xhr
}

function abortAllRequests() {
  requests.forEach(function(xhr, index) {
    xhr.abort()
  })
}

var requests = [];

requests.push(request(123), request(456));

abortAllRequests();

jsfiddle https://jsfiddle.net/onguym5y/

guest271314
  • 1
  • 15
  • 104
  • 177
  • Amazing thanks, your first solution worked for me before I saw your second one which I haven't yet tried, but it seems more complex - is there any benefit to using it instead of `if (!$.active) {`? – The Codesee Sep 20 '17 at 20:21
  • The second option attaches jQuery `ajaxComplete` to `document` if an `$.ajax()` request is active, within `ajaxComplete` handler the next `$.ajax()` request is called when previous `$.ajax()` call completes. – guest271314 Sep 20 '17 at 20:24
  • I've tried your second solution, but after the user has stopped typing, no more "new" results seem to appear. Instead, the div just empties after showing the results for the first letter. The AJAX request is still made though, but no data is send - it says 'undefined' – The Codesee Sep 22 '17 at 15:33
  • Ideally, I would like to cancel all remaining ajax requests in my original code in the function `if(this.value.length < 1) {` – The Codesee Sep 22 '17 at 16:24
  • Can you create a jsfiddle https://jsfiddle.net to demonstrate? One solution would be use include an `` element at HTML for user to press when the user decides to make request. – guest271314 Sep 23 '17 at 04:01
  • What would you like me to show in the JsFIddle? Unfortunately I wouldn't really like a submit button. Is it not possible to cancel all ajax requests inside the `$("input#enter").keyup(function() {` function? – The Codesee Sep 23 '17 at 11:36
  • You can store `XMLHttpRequest()` within an array and call `.abort()` on each element. Aborting `fetch()` calls is not yet fully implemented https://github.com/whatwg/fetch/issues/607 – guest271314 Sep 23 '17 at 11:40
  • Would you be able to include an example of that please? – The Codesee Sep 23 '17 at 12:05
  • I don't like the `$.active` answer at all, for several reasons: I don't see `$.active` in the jQuery documentation, so I'd be uneasy about using it. I'm assuming it tells you whether any ajax requests are pending. So while it could be used to throttle ajax requests, this bug really only requires one ajax request - it's a race condition between a single request and a user action (although probably hard to reproduce without creating a backlog of requests). Also, it could cancel the last request and end up displaying the wrong data. And it would be affected by any other code that uses ajax. – David Knipe Sep 23 '17 at 16:08
  • @DavidKnipe There are several undocumented jQuery methods. OP has decided to make an ajax request at each `keyup` event. The approach using `$.active` is intended to make a single request at a time. – guest271314 Sep 23 '17 at 16:11
  • Sorry, I misread your answer. I got to "You can also include...", thought that was the end of the first answer, and didn't read any more. But still, if the `keyup` handler is called three times in quick succession, the last two calls will send their ajax requests together after the first one has returned. – David Knipe Sep 23 '17 at 16:32
  • @DavidKnipe Suggested that OP use an alternative approach, that is for user to click a button when they want to perform a request. That is not what OP has selected to do. Perhaps post your the solution that you would use at an Answer? – guest271314 Sep 23 '17 at 16:50
  • I don't understand... I never said anything about buttons. Unless you misread the post that I've deleted. Regardless, I will post my answer as you requested. – David Knipe Sep 23 '17 at 16:58
  • @DavidKnipe Did not state you mentioned buttons. Stated that was this users' suggestion to OP – guest271314 Sep 23 '17 at 17:14
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/155160/discussion-between-david-knipe-and-guest271314). – David Knipe Sep 23 '17 at 17:18
0

You talk about aborting ajax requests. It would be sufficient to wait until the request returns and then simply do nothing. Yes, if you were doing a lot of large requests it might improve performance if you cancelled them. But that means using jqXhr objects, and personally I prefer to stick to jQuery where possible.

You could have a variable telling you how up-to-date the #display is. It would store the time of sending of the last ajax request that was used to update it. If you get a response from an earlier request, ignore it.

var lastUpdateTime = 0;

$("input#enter").keyup(function() {
    var now = new Date().getTime();
    if(this.value.length < 1) {
        $("#display").empty();
        lastUpdateTime = now;
    }else{ 
        $.ajax({
            type: "POST",
            url: "getdata.php",
            data: "title=" + this.value,
            success: function(data) {
                if (now < lastUpdateTime) {
                    return;
                }
                $("#display").empty(); 
                $("#display").html(data);
                lastUpdateTime = now;
            }
        });
    } 
});
David Knipe
  • 3,417
  • 1
  • 19
  • 19