Kill/terminate ajax request if a new one is fired - javascript

I've built a javascript application which has a graph with some dropdown filters that users are able to change. The dropdowns all have event listeners which submit a server request to get the data (via a jquery ajax call) and then graphs the data. The issue is if the user uses the keyboard to quickly go through many different elements of the dropdown.
The server call takes roughly a second so if they quickly scroll through say 20, this can lead to a buildup. 20 different requests to the server are created, and then there's not even a guarantee that the last piece of code to be executed on server request success will be the most current filter.
So my question is what is the best way when a filter is changed to kill all other asynchronous processes? Here's the relevant code:
$("#filter").change(function(d) {
getData();
} //end of if
});
function getData() {
...
$.ajax({
url: myUrl
type: "GET",
dataType: "json",
success: function(d) {
[do some stuff]
} //end of success function
}); //end of ajax
} //end of getData

Save all the Ajax calls you wish to abort into some variable then you can abort the last call.
This is a very common practice when some ajax call might happen many times, before the ones before it had a chance to finish.
function getData(){
$filter.data('REQ') && $filter.data('REQ').abort(); // abort the last request before creating a new one
return $.ajax({
url:myUrl
type:"GET",
dataType:"json",
success:function(d){
[do some stuff]
}
})
}
var $filter = $("#filter");
$filter.on('change', function(d) {
$filter.data('REQ', getData())
});
Of-course this is a very simplified manner of code and you should re-write this in a more structured way, but it gives you an idea of how to cache the last ajax request and then you have the power to abort it before sending a new call.
By the way, your title has nothing to do with the question. You are asking how to handling a sequence of ajax calls but asking about events. this question has nothing to do with events, so you should change the title to fit the problem.
Update regarding what #t.niese had said in the comments:
Throttling the requests on the client side is also a good play to do, since the server cannot really know if the client has aborted the request or not, and if it's a resource-demanding request, it should be throttled.
BUT, I would suggest throttling the requests on the server-side and not on the client-side, if possible, because client-side throttling could be by-passed and is not 100% reliable, and it "costs" the same amount of time to do that on the server-side.
Can a http server detect that a client has cancelled their request?

I would drop all attempts initiate a new request calling getData while a current request is running, and only send the last getData attempt as soon as the current request is finished. This will ensure that the server load wont become unnecessarily high, because only one request will run.
var currentRequest;
var resubmitRequest;
function getData() {
// only start a new request id no current request is running
if (!currentRequest) {
resubmitRequest = false;
currentRequest = Promise.resolve($.ajax({
url: myUrl
type: "GET",
dataType: "json"
})); //end of ajax
currentRequest
.then((d) => {
[do some stuff]
})
.finally(() => {
// if the current request finished and another attempt to request data
// happened while this request was running then call getData again
if (resubmitRequest) {
getData()
}
})
} else {
// store the information that the data has to be requested
// another time after the currently running request finished
resubmitRequest = true;
}
return currentRequest;
} //end of getData

you can simply pause it for example to 500mil seconds and then run 'last' change and do that ajax call..
$("#filter").change(function(d) {
if( $.active > 0 ) return false;
// do not run new ajax if last one is not finished
clearTimeout( window.timer );
// if new 'change' evt is raised in less then 500 mils
// then clear it and run 'last' one
window.timer = setTimeout( function(){
getData();
}, 500 );
});
in case if user changes it while doing ajax, return false it :)

With little effort you can code your own handler for these cases:
function handler(ms, fn) {
var eventId;
return function () {
// if there is an event programmed, kill it
clearTimeout(eventId);
// and program the new one (replace the event)
eventId = setTimeout(fn);
// if passed the time no event was programmed, setTimeout is going to execute the function
}
}
// execute getData if no event is fired in a 1000ms interval (the user stopped typing)
// getData is the same that you have
var fn = handler(1000, getData);
$("#filter").change(fn);

Related

OfficeJS Caching

I'm really curious if someone can better explain the internal workings of excel's addin caching of javascript functions? I'm running a flask app on my own internal website behind a SSL cert. My addin pulls in this functionfile.html, and makes an ajax call back to mysite:
<script>
// After the Office library builds, it will look for Office.initialize
// which must be passed a function. It doesnt have to do anything though.
Office.initialize = function (reason){
$(document).ready(function(){
registerBindings();
});
}
function getData(){
return Excel.run( function (context) {
// request bindings
var state_nbr = context.workbook.bindings.getItem("state_nbr").getRange().load("values");
// and the rest for the ajax call
return context.sync().then( function () {
$.ajax({
type: "GET",
url: "https://example.com/excel/function",
data: {
"state_nbr": state_nbr.values[0][0]
}
}).done( function (data){
context.workbook.bindings.getItem("test").getRange().values = [["done"]];
return context.sync();
}).fail( function (result){
context.workbook.bindings.getItem("test").getRange().values = [["fail"]];
return context.sync();
});
});
});
}
</script>
When I click my button, I can see the request with the right payload going to example.com/excel/function, which is a flask route that pumps out a bunch of CLI junk (hundreds of logging commands).
What gets weird though, is that after that first click every time I click the button I don't get any new ajax requests, I only get a request for the functionfile.html. But SheetA1 still pops up "done".
I thought this was just storing the results in cache, but even with flask running in debug mode, if I change functionfile.html, say [["done"]] to [["finished"]], no new ajax call is detected in my logs. BUT THE SHEET UPDATES?!

Aborting / canceling running AJAX calls before execute new AJAX in JS

I've never done this type of manipulation of AJAX calls (to stop/abort/cancel or ignore? already running AJAX calls before the execution of a new one) before so I really don't understand how to do that and would appreciate some direction.
I have a page in my app where I make a number of AJAX calls to fill dynamically the data in my table (Object Name, Object Fit, Object Progress) when the page loads. For example, there are 5 rows in the table. So I call
$.post("/getFit", {objectId: objectId}, function (result) { manipulation with result }
and
$.post("/getProgress", {objectId: objectId}, function (result) { manipulation with result }
5 times each in the loop -- one for each of the objects.
The first column of my table has links to more detail on the object, and clicking on them I call another AJAX:
$(document).off('click', '.js_object').on('click', '.js_object', function (e) {
var objectId = $(this).attr("id")
$.post("/viewObject", {objectId: objectId}, function (result) {document.getElementById("main_window_content").innerHTML = result; });
})
The problem is that the browser will not render the results of the last AJAX call (/viewObject) until it has received the results of all of the previous calls (/getFit x5 and /getProgress x5).
As a result, a user that wants to drill into the detail on an object needs to wait until the AJAX calls for the other objects are complete before they see anything.
So I struggle with how to stop/abort/cancel (or ignore?) "/getProgress" and "/getFit" so we can fully execute "/viewObject" and view the results of it.
I would very much appreciate your help.
Use xhr.abort() to kill the xhr requests as shown in the below code in JS. I believe there is ajax.abort(); in JQuery
var xhr = $.ajax({
type: "POST",
url: "XXX.php",
data: "name=marry&location=London",
success: function(msg){
alert( "The Data Saved: " + msg );
}
});
//kill the request
xhr.abort()
If you want execute one ajax after another, and you need all requests to work to show the final result, you can use .done():
$.ajax({
url:'/getFit',
data:{objectId:objectId}
})
.done(function(data){
//do something with the results then call /getProgress
$.ajax({
url:'/getProgress',
data:{objectId:objectId}
})
.done(function(data){
//do something with the results then call /viewObject
$.post("/viewObject"....
})
});
That way you only show /viewObject if the others calls were successfull

Web Workers creation inside Ajax callback

I saw that's possible to do ajax requests inside a web worker, but I want to do the ajax call via jQuery (outside worker, of course), and after this, pass the result of the callback to the worker.
I made some tests and this works, but I want to know if there's something wrong with it (memory leaks, incompatibility, instability):
$.ajax
({
type: 'GET',
url : 'http://192.168.0.2/json.php',
data: requestDataObj,
dataType: 'json'
}).success(function(jsonResult)
{
var jSonWorker = new Worker('http://localhost/webmail/responsive/js/workers.js');
jSonWorker.postMessage(jsonResult);
jSonWorker.onmessage = function(event)
{
alert(event.data)
}
});
As you can see, I pass the jsonResult to the worker, where I do something with it and post a message back to the main thread. Anything wrong with this?
The only problem I see is that you're assuming any browser has support for window.Worker, which is not the case.
In case blocking the application is feasible — say the computation you're expecting the worker to do is light — you could paste the entire code of the worker inside the ajax callback [1].
Thus
...success(function (res) {
if (window.Worker) {
var worker = new Worker(...);
worker.onmessage = function (e) { /* ... */ };
worker.postMessage(res);
}
else {
// Write here the same code the worker
// is supposed to execute "on" res.
}
});
Course you will lose the performance improvement you gained with two threads.
[1] As done here by #afshinm.

jQuery listener for a server update

I am working on an web app that communicates with a server to update it's database. Problem is the update can take anywhere from a few milliseconds to even tens of seconds at a time with a big update. I would like to introduce some listener on the client side that would wait for server to finish it's last job and inform the client to force a refresh (by refresh I mean calling an ajax function that asks server for new data, destroys present data table and grows a new one).
Until now I just called the refresh on the client side with some delay after making any changes to the server database, but I'd prefer to "grey out" the table on the client side, until it gets a response from the server.
I am a newbie in the jQuery area, can someone point me to how I could achieve that?
Why don't you just make an AJAX call using $.ajax(), and then hook into the deferred done() function. That way, when your DB update is done, you can handle everything inside the done() function.
For example:
$.ajax({
'type': 'get',
'url': 'update_db.php'
}).done(function() {
// The PHP has executed and the call has returned.
// 'Ungrey' your items here...
});
You can poll regurlarly the server for updates:
var job = $.Deferred();
var POLL_INTERVAL_MSEC = 5000;
var timer = setInterval( function() {
$.ajax('status/isfinshed').done( function( response ) {
if ( response.finished ) {
clearInterval( timer );
job.resolve();
}
});
} , POLL_INTERVAL_MSEC);
job.done( function() {
//success code
});

AJAX read data in a progressive manner not just when it's completed

I like to create a progress bar for my ajax calls.
For this I can make my server side script to return the state of it's progress.
So I need for javascript to read this progress level and show it.
Is it possible or am I on the wrong road?
You could try something like this (some pseudocode, assuming jQuery, since you've tagged the question as such):
var poll;
$.ajax({
url: 'your_ajax_script',
beforeSend: function(){ // set up out-of-band status polling
poll = setInterval( function(){
$.get('your_script_that_returns_status',
function(data){
update_progressbar(data);
});
}, 1000 ); // update every second?
},
success: function(data) {
clearInterval( poll ); // stop polling
finalize_or_hide_progressbar(); // clean up
do_something_with( data ); // your "done" logic
}
});
Either poll at intervals, returning the payload on the final successful call, or run two AJAX calls - the first retrieving the actual data, and the second polling for update percentage.

Categories