Good afternoon guys -
Is there a well known way to check if a $.ajax call has finished?
-- FOR INSTANCE --
Let's say I'm using a $.ajax call to load in a large number of leaflet polygons from a .geojson file (asynchronously as a layer group). Under normal circumstances, this happens almost immediately - but the user has elected to load a large job this time around. The user has assumed that the set of polygons has been loaded and attempts to do something with this group of layers prematurely - only to find that nothing happens (the layer group doesn't actually exist yet in the browser).
My intuition (I'm new to web development) is to have some sort of global flag that's set for the dependent algorithm to check. We would set the flag prior to loading the layers, and then change it to some other value in the .done({}) section of the $.ajax call.
-- UPDATE --
I've reworked the code to allow users to choose whether or not they wish to retry the request, and also force the browser to wait some time before retrying the request.
However, I've also found the the issue seems to be with Chrome. Firefox appears to be able to handle the $.ajax.always callback as soon as it finishes (in other words, the $.ajax.always callback will interrupt the regular flow of javascript).
Chrome appears to block the $.ajax.always callback and only lets it execute after all other javascript has finished running (no interrupt).
This particular question stemmed from a debug case that automated user input from within a loop - and since Chrome doesn't call the $.ajax.always callback until the current process is complete, the process isn't marked as completed.
Example Code:
procBox = []; // Global scope, stands for Process Box
function loadPolygons() {
procBox["loadPolygons"] = "running";
$.ajax({
// ajax things here
}).done(function() {
procBox["loadPolygons"] = "done";
}).fail(function() {
// failure stuff here
});
}
function dependentFunction() {
if procBox["loadPolygons"] === "done") {
// The meat of the function (dependentFunction things here)
} else {
// Backup case that allows the browser to retry the request
/* --
* If this fires, the server is still trying to process the
* ajax request. The user will be prompted to retry the request,
* and upon agreement, the function will be called again after
* 1 second passes.
*/
var msg = "Oops! It looks like we're still fetching your "
+ "polygons from the server. Press OK to retry.";
if (confirm(msg)) {
setTimeout(dependentFunction, 1000);
}
}
}
This approach seems to work well in Firefox - the alert() stops JavaScript execution and gives it a chance for the .done({}) callback to occur. But for some reason, the while loop never allows the .done({}) callback to complete in Chrome!
Does anyone know of a better approach for this than using flags or async: false?
I appreciate any answers and knowledge out there!
There are numerous ways to do :
as already sugegsted you can use
since you use jQuery, you can use custom events https://learn.jquery.com/events/introduction-to-custom-events/:
$(document).on("myajax:done", function(){ dependentFunction();})
$.ajax({...}).done(function(){
$(document).trigger("myajax:done")
});
or even global ajax events
https://api.jquery.com/category/ajax/global-ajax-event-handlers/
but really consider why not to do something like
procBox = {onLoadPolygons:dependentFunction}; // Global scope
function loadPolygons() {
$.ajax({
// ajax things here
}).done(function() {
procBox["onLoadPolygons"]();
}).fail(function() {
// failure stuff here
});
}
function dependentFunction() {
alert("Please wait for the polygons to load");
dependentFunctionThings();
}
function dependentFunctionThings(){
// Do dependent function things...
}
UPD:
if you ensist on your structure, and still want to use blocking function
use setInterval to perform check
function dependentFunction() {
var h = setInterval(function() {
if (procBox["loadPolygons"] == "done") {
clearInterval(h);
// Do dependent function things...
}
}, 100);
}
Or wrap it up into a Promise (http://caniuse.com/#feat=promises)
function dependentFunction() {
(new Promise(function(resolve) {
var h = setInterval(function(){
if (procBox["loadPolygons"] == "done") {
clearInterval(h);resolve();
}
}, 100);
})).then(function(){
// Do dependent function things...
});
}
But I still believe that something wrong in your structure
From the docs :
.ajaxComplete()
Whenever an Ajax request completes, jQuery triggers the ajaxComplete event. Any and all handlers that have been registered with the .ajaxComplete() method are executed at this time.
http://api.jquery.com/ajaxcomplete/
Related
I have a bootstrapped extension which interacts with the chrome part of Firefox (i.e. even before the content loads), and needs to query an SQLite database for some check. I would prefer a sync call. But, since a sync call is bad in terms of performance and can cause possible UI issues, I need to make an async DB call.
My use case is such:
Make aysnc call to database
Once completed do further processing
Now, this can be easily handled by placing 'further processing' part in handleCompletion part of executeAsync function.
But, I want the 'further processing' to be done irrespective of this statement being executed i.e. This DB lookup may or may not happen. If it doesn't happen well and good, go ahead. If it does I need to wait.
So, I am using a flag based strategy; I set a flag handleCompletionCalled in handleError & handleCompletion callback to true.
In the further processing part, I do a
while(handleCompletionCalled) {
// do nothing
}
//further processing
Is this a good strategy or can I do something better ( I don't really want to use Observers, etc. for this since I have many such cases in my entire extension and my code will be filled with Observers)?
Using a while loop to wait is a seriously Bad Idea™. If you do, the result will be that you hang the UI, or, at a minimum, drive CPU usage through the roof by rapidly running though your loop a large number of times as fast as possible.1
The point about asynchronous programming is that you start an action and then another function, a callback, is executed once the activity is completed, or fails. This either allows you to start multiple actions, or to relinquish processing to some other part of the overall code. In general, this callback should handle all activity that is dependent on the completion of the asynchronous action. The callback function, itself, does not have to include the code to do the other processing. After it has done what needs to happen in response to the async action completing, it can just call another function like doOtherProcessing().
If you launch multiple asynchronous, actions you can then wait for the completion of all of them by having flags for each task and a single function that is called at the end of all the different callback functions like:
function continueAfterAllDone(){
if(task1Done && task2Done && task3Done && task4Done) {
//do more processing
}else{
//Not done with everything, yet.
return;
}
}
This could be extended to an arbitrary number of tasks by using an array, or task queue, which the function then checks to see if all of those are completed rather than a hard-coded set of tasks.
Waiting:
If you are going to have another processing path which executes, but then must wait for the completion of the asynchronous action(s), you should have the wait performed by setting up a timer, or interval. You then yield the processor for a specified period of time until you check again to see if the conditions you need to proceed have occurred.
In a bootstrapped add-on, you will probably need to use the nsITimer interface to implement a timeout or interval timer. This is needed because at the time you are running your initialization code it is possible that no <window> exists (i.e. there may be no possibility to have access to a window.setTimeout()).
If you are going to implement a wait for some other task, you could do it something like:
const Cc = Components.classes;
const Ci = Components.interfaces;
var asyncTaskIsDone = false;
var otherProcessingDone = false;
// Define the timer here in case we want to cancel it somewhere else.
var taskTimeoutTimer;
function doStuffSpecificToResultsOfAsyncAction(){
//Do the other things specific to the Async action callback.
asyncTaskIsDone = true;
//Can either call doStuffAfterOtherTaskCompletesOrInterval() here,
// or wait for the timer to fire.
doStuffAfterBothAsyncAndOtherTaskCompletesOrInterval();
}
function doStuffAfterBothAsyncAndOtherTaskCompletesOrInterval(){
if(asyncTaskIsDone && otherProcessingDone){
if(typeof taskTimeoutTimer.cancel === "function") {
taskTimeoutTimer.cancel();
}
//The task is done
}else{
//Tasks not done.
if(taskTimeoutTimer){
//The timer expired. Choose to either continue without one of the tasks
// being done, or set the timer again.
}
//}else{ //Use else if you don't want to keep waiting.
taskTimeoutTimer = setTimer(doStuffAfterBothAsyncAndOtherTaskCompletesOrInterval
,5000,false)
//}
}
}
function setTimer(callback,delay,isInterval){
//Set up the timeout (.TYPE_ONE_SHOT) or interval (.TYPE_REPEATING_SLACK).
let type = Ci.nsITimer.TYPE_ONE_SHOT
if(isInterval){
type = Ci.nsITimer.TYPE_REPEATING_SLACK
}
let timerCallback = {
notify: function notify() {
callback();
}
}
var timer = Cc["#mozilla.org/timer;1"].createInstance(Ci.nsITimer);
timer.initWithCallback(timerCallback,delay,type);
return timer;
}
function main(){
//Launch whatever the asynchronous action is that you are doing.
//The callback for that action is doStuffSpecificToResultsOfAsyncAction().
//Do 'other processing' which can be done without results from async task here.
otherProcessingDone = true;
doStuffAfterBothAsyncAndOtherTaskCompletesOrInterval();
}
Initialization code at Firefox startup:
The above code is modified from what I use for delaying some startup actions which do not have to be done prior to the Firefox UI being displayed.
In one of my add-ons, I have a reasonable amount of processing which should be done, but which is not absolutely necessary for the Firefox UI to be shown to the user. [See "Performance best practices in extensions".] Thus, in order to not delay the UI, I use a timer and a callback which is executed 5 seconds after Firefox has started. This allows the Firefox UI to feel more responsive to the user. The code for that is:
const Cc = Components.classes;
const Ci = Components.interfaces;
// Define the timer here in case we want to cancel it somewhere else.
var startupLaterTimer = Cc["#mozilla.org/timer;1"].createInstance(Ci.nsITimer);
function startupLater(){
//Tasks that should be done at startup, but which do not _NEED_ to be
// done prior to the Firefox UI being shown to the user.
}
function mainStartup(){
let timerCallback = {
notify: function notify() {
startupLater();
}
}
startupLaterTimer = startupLaterTimer.initWithCallback(timerCallback,5000
,Ci.nsITimer.TYPE_ONE_SHOT);
}
Note that what is done in startupLater() does not, necessarily, include everything that is needed prior to the ad-on being activated by the user for the first time. In my case, it is everything which must be done prior to the user pressing the add-on's UI button, or invoking it via the context menu. The timeout could/should be longer (e.g. 10s), but is 5s so I don't have to wait so long for testing while in development. Note that there are also one-time/startup tasks that can/should be done only after the user has pressed the add-on's UI button.
1. A general programming issue here: In some programming languages, if you never yield the processor from your main code, your callback may never be called. In such case, you will just lock-up in the while loop and never exit.
Is there a way, raw JavaScript or via a plugin, that will detect if a given function has an AJAX call inside? I've been working with API's recently, and it often occurs to me that some of my codes ran "a little bit earlier" than it supposed to because the previous script contains an async AJAX call. For instance:
var doSomething = function(by, activity) {
// Call something from the API of from another developer
// or a function() from a 3rd party JS library.
var anonFunction = API.package[by][activity]; // or anonFunction = 3rpPartyJS();
if(anonFunction === undefined) {
return "skipped";
}
else {
var response = anonFunction();
// The problem is that the code below will execute not knowing whether
// there are still AJAX call/s pending within the var anonFunction
// initialization. If there is indeed an AJAX call (which sometimes there
// aren't, depends on the <function name> called above), the script below
// will be executed a little bit earlier than it should be.
if(response["success"] === true) {
... // do something
return "done";
}
else {
alert("Failed to call function " + by + "." + me);
return "fail";
}
}
}
$(function() {
// This is successful.
doSomething("me", "aSyncFunction");
// This returns "fail", even thought the response returns
// successful (after some X milliseconds later).
doSomething("me", "anAsyncFunction");
});
I know how to do AJAX promises on JQuery, although it could take me forever to re-code the AJAX call inside those external JS files. Plus configuring those external JS files would be dangerous, I don't even know if there are other pages that use those JS, that may be affected. Dropping the "re-coding" as solution, I'm looking for an alternative function to check if:
There is/are AJAX call inside a function (, and subsequently, every other subfunction/s that is/are been called inside that function).
And if there is, would make the browser "wait" until the AJAX calls finish, before running the next line.
Currently, I'm using a clumsy solution that utilizes something similar to sleep() function, but I know that is not a good solution. I need something like:
}
else {
var response = anonFunction();
while($.activeAJAX().length !== 0) // <<< something like this
// will do nothing and wait.
}
One of my javascript function is processing millions of data and it is called ~1 time every second from a hardware event. Then the web browser is idle in that function processing.
I tried to set a flag for running (or not running) that function:
if (!is_calculating)
is_calculating = true;
else
return;
my_function(); // do heavy stuff
is_calculating = false;
but it's not working, because it is entering into the code and the web browser enter in an idle status until is finishing. When it is returning, the flag is always OK, because it finished the // do heavy stuff
Can I do something for this behavior? I'd like to jump function execution if a flag is set.
The problem is, by default javascript runs in a single thread on browsers, so your code is executing completely before it even begins to process the next call, resulting in is_calculating always being false when the function is called. One workaround you could use (not the cleanest solution in the world), is to divide your monolithic 'heavy stuff' function into a number of smaller functions and have them call each other with setTimeout(nextFunc, 1). Having them call each other that way gives the browser a moment to do what it needs to do, and additionally call your function again if that's what it's doing. This time, because your function is called in the 'middle' of it already being executed, is_calculating is still going to be true, and the call will return at the beginning like you expect it to. Note this solution probably isn't as preferable as the Web Workers solution, but it is simpler.
function sleep(millis) {
var date = new Date()
var curDate = null
do { curDate = new Date() }
while(curDate-date < millis)
}
function reallyLong() {
if(!reallyLong.flag) {
reallyLong.flag = true
} else {
console.log("Not executing")
return
}
sleep(250)
setTimeout(reallyLong2, 1)
function reallyLong2() {
sleep(250)
setTimeout(reallyLong3, 1)
}
function reallyLong3() {
sleep(250)
setTimeout(reallyLong4, 1)
}
function reallyLong4() {
sleep(250)
console.log("executed")
reallyLong.flag = false
}
}
If you define all your consecutive functions inside the primary function, it also allows them all to access the same data simply and easily.
The only catch now is if your function was returning some value, you need to rewrite it to either return a promise (Either of your own design or using a library like Q), or accept a callback as a parameter that the last function in the 'chain' will call with the return value as a parameter.
Note that the sleep function above is a hack, and awful, and terrible, and should never be used.
By default JavaScript execution in browsers is not concurrent. This means, usually there can be only one currently executing piece of code.
You have to use Web Workers API to make you code run concurrently.
I have a webpage, in which a certain Ajax event is triggered asynchronously. This Ajax section could be called once or more than once. I do not have control over the number of times this event is triggered, nor the timing.
Also, there is a certain code in that Ajax section that should run as a critical section, meaning, when it is running, no other copy of that code should be running.
Here is a pseudo code:
Run JavaScript or jQuery code
Enter critical section that is Ajax (when a certain process is waiting for a response callback, then do not enter this section again, until this process is done)
Run more JavaScript or jQuery code
My question is, how can I run step 2 the way described above? How do I create/guarantee a mutual exclusion section using JavaScript or jQuery.
I understand the theory (semaphores, locks, ...etc.), but I could not implement a solution using either JavaScript or jQuery.
EDIT
In case you are suggesting a Boolean variable to get into the critical section, this would not work, and the lines below will explain why.
the code for a critical section would be as follows (using the Boolean variable suggestions):
load_data_from_database = function () {
// Load data from the database. Only load data if we almost reach the end of the page
if ( jQuery(window).scrollTop() >= jQuery(document).height() - jQuery(window).height() - 300) {
// Enter critical section
if (window.lock == false) {
// Lock the critical section
window.lock = true;
// Make Ajax call
jQuery.ajax({
type: 'post',
dataType: 'json',
url: path/to/script.php,
data: {
action: 'action_load_posts'
},
success: function (response) {
// First do some stuff when we get a response
// Then we unlock the critical section
window.lock = false;
}
});
// End of critical section
}
}
};
// The jQuery ready function (start code here)
jQuery(document).ready(function() {
var window.lock = false; // This is a global lock variable
jQuery(window).on('scroll', load_data_from_database);
});
Now this is the code for the lock section as suggested using a Boolean variable. This would not work as suggested below:
The user scrolls down, (and based on the association jQuery(window).on('scroll', load_data_from_database); more than one scroll event is triggered.
Assume two scroll events are triggered right at almost the same moment
Both call the load_data_from_database function
The first event checks if window.lock is false (answer is true, so if statement is correct)
The second event checks if window.lock is false (answer is true, so if statement is correct)
The first event enters the if statement
The second event enters the if statement
The first statement sets window.lock to true
The second statement sets window.lock to true
The first statement runs the Ajax critical section
The second statement runs the Ajax critical section.
Both finish the code
As you notice, both events are triggered almost at the same time, and both enter the critical section. So a lock is not possible.
I think the most helpful information you provided above was your analysis of the locking.
The user scrolls down, (and based on the association jQuery(window).on('scroll', load_data_from_database); more than one
scroll event is triggered.
Assume two scroll events are triggered right at almost the same moment
Both call the load_data_from_database function
The first event checks if window.lock is false (answer is true, so if statement is correct)
The second event checks if window.lock is false (answer is true, so if statement is correct)
Right away this tells me that you have come to a common (and quite intuitive) misunderstanding.
Javascript is asynchronous, but asynchronous code is not the same thing as concurrent code. As far as I understand, "asynchronous" means that a function's subroutines aren't necessarily explored in depth-first order as we would expect in synchronous code. Some function calls (the ones you are calling "ajax") will be put in a queue and executed later. This can lead to some confusing code, but nothing is as confusing as thinking that your async code is running concurrently. "Concurrency" (as you know) is when statements from different functions can interleave with one another.
Solutions like locks and semaphores are not the right way to think about async code. Promises are the right way. This is the stuff that makes programming on the web fun and cool.
I'm no promise guru, but here is a working fiddle that (I think) demonstrates a fix.
load_data_from_database = function () {
// Load data from the database. Only load data if we almost reach the end of the page
if ( jQuery(window).scrollTop() >= jQuery(document).height() - jQuery(window).height() - 300) {
console.log(promise.state());
if (promise.state() !== "pending") {
promise = jQuery.ajax({
type: 'post',
url: '/echo/json/',
data: {
json: { name: "BOB" },
delay: Math.random() * 10
},
success: function (response) {
console.log("DONE");
}
});
}
}
};
var promise = new $.Deferred().resolve();
// The jQuery ready function (start code here)
jQuery(document).ready(function() {
jQuery(window).on('scroll', load_data_from_database);
});
I'm using a global promise to ensure that the ajax part of your event handler is only called once. If you scroll up and down in the fiddle, you will see that while the ajax request is processing, new requests won't be made. Once the ajax request is finished, new requests can be made again. With any luck, this is the behaviour you were looking for.
However, there is a pretty important caveats to my answer: jQuery's implementation of promises is notoriously broken. This isn't just something that people say to sound smart, it is actually pretty important. I would suggest using a different promise library and mixing it with jQuery. This is especially important if you are just starting to learn about promises.
EDIT: On a personal note, I was recently in the same boat as you. As little as 3 months ago, I thought that some event handlers I was using were interleaving. I was stupefied and unbelieving when people started to tell me that javascript is single-threaded. What helped me is understanding what happens when an event is fired.
In syncronous coding, we are used to the idea of a "stack" of "frames" each representing the context of a function. In javascript, and other asynchronous programming environments, the stack is augmented by a queue. When you trigger an event in your code, or use an asynchronous request like that $.ajax call, you push an event to this queue. The event will be handled the next time that the stack is clear. So for example, if you have this code:
function () {
this.on("bob", function () { console.log("hello"); })
this.do_some_work();
this.trigger("bob");
this.do_more_work();
}
The two functions do_some_work and do_more_work will fire one after the other, immediately. Then the function will end and the event you enqueued will start a new function call, (on the stack) and "hello" will appear in the console. Things get more complicated if you trigger an event in your handler, or if you trigger and event in a subroutine.
This is all well and good, but where things start to get really crappy is when you want to handle an exception. The moment you enter asynchronous land, you leave behind the beautiful oath of "a function shall return or throw". If you are in an event handler, and you throw an exception, where will it be caught? This,
function () {
try {
$.get("stuff", function (data) {
// uh, now call that other API
$.get("more-stuff", function (data) {
// hope that worked...
};
});
} catch (e) {
console.log("pardon me?");
}
}
won't save you now. Promises allow you to take back this ancient and powerful oath by giving you a way to chain your callbacks together and control where and when they return. So with a nice promises API (not jQuery) you chain those callbacks in a way that lets you bubble exceptions in the way you expect, and to control the order of execution. This, in my understanding, is the beauty and magic of promises.
Someone stop me if I'm totally off.
I would recommend a queue which only allows one item to be running at a time. This will require some modification (though not much) to your critical function:
function critical(arg1, arg2, completedCallback) {
$.ajax({
....
success: function(){
// normal stuff here.
....
// at the end, call the completed callback
completedCallback();
}
});
}
var queue = [];
function queueCriticalCalls(arg1, arg2) {
// this could be done abstractly to create a decorator pattern
queue.push([arg1, arg2, queueCompleteCallback]);
// if there's only one in the queue, we need to start it
if (queue.length === 1) {
critical.apply(null, queue[0]);
}
// this is only called by the critical function when one completes
function queueCompleteCallback() {
// clean up the call that just completed
queue.splice(0, 1);
// if there are any calls waiting, start the next one
if (queue.length !== 0) {
critical.apply(null, queue[0]);
}
}
}
UPDATE: Alternative solution using jQuery's Promise (requires jQuery 1.8+)
function critical(arg1, arg2) {
return $.ajax({
....
});
}
// initialize the queue with an already completed promise so the
// first call will proceed immediately
var queuedUpdates = $.when(true);
function queueCritical(arg1, arg2) {
// update the promise variable to the result of the new promise
queuedUpdates = queuedUpdates.then(function() {
// this returns the promise for the new AJAX call
return critical(arg1, arg2);
});
}
Yup, the Promise of cleaner code was realized. :)
You can wrap the critical section in a function and then swap the function so it does nothing after first run:
// this function does nothing
function noop() {};
function critical() {
critical = noop; // swap the functions
//do your thing
}
Inspired by user #I Hate Lazy Function in javascript that can be called only once
In tornado we have gen module, that allows us to write constructions like this:
def my_async_function(callback):
return callback(1)
#gen.engine
def get(self):
result = yield gen.Task(my_async_function) #Calls async function and puts result into result variable
self.write(result) #Outputs result
Do we have same syntax sugar in jquery or other javascript libraries?
I want something like this:
function get_remote_variable(name) {
val = $.sweetget('/remote/url/', {}); //sweetget automatically gets callback which passes data to val
return val
}
You describe the function as "my_async_function", but the way you use it is synchronous rather than asynchronous.
Your sample code requires blocking -- if "my_async_function" were truly asynchronous (non-blocking), the following line of code self.write(result) would execute immediately after you called "my_async_function". If the function takes any time at all (and I mean any) to come back with a value, what would self.write(result) end up writing? That is, if self.write(result) is executed before result ever has a value, you don't get expected results. Thus, "my_async_function" must block, it must wait for the return value before going forward, thus it is not asynchronous.
On to your question specifically, $.sweetget('/remote/url/', {}): In order to accomplish that, you would have to be able to block until the ajax request (which is inherently asynchronous -- it puts the first A in AJAX) comes back with something.
You can hack a synchronous call by delaying the return of sweetget until the XHR state has changed, but you'd be using a while loop (or something similar) and risk blocking the browser UI thread or setting off one of those "this script is taking too long" warnings. Javascript does not offer threading control. You cannot specify that your current thread is waiting, so go ahead and do something else for a minute. You could contend with that, too, by manually testing for a timeout threshold.
By now one should be starting to wonder: why not just use a callback? No matter how you slice it, Javascript is single-threaded. No sleep, no thread.sleep. That means that any synchronous code will block the UI.
Here, I've mocked up what sweetget would, roughly, look like. As you can see, your browser thread will lock up as soon as execution enters that tight while loop. Indeed, on my computer the ajax request won't even fire until the browser displays the unresponsive script dialog.
// warning: this code WILL lock your browser!
var sweetget = function (url, time_out) {
var completed = false;
var result = null;
var run_time = false;
if (time_out)
run_time = new Date().getTime();
$.ajax({
url: url,
success: function(data) {
result = data;
completed = true;
},
error: function () {
completed = true;
}
}); // <---- that AJAX request was non-blocking
while (!completed) { // <------ but this while loop will block
if (time_out) {
if (time_out>= run_time)
break;
run_time = new Date().getTime();
}
}
return result;
};
var t = sweetget('/echo/json/');
console.log('here is t: ', t);
Try it: http://jsfiddle.net/AjRy6/
Versions of jQuery prior to 1.8 support sync ajax calls via the async: false setting. Its a hack with limitations (no cross-domain or jsonp, locks up the browser), and I would avoid it if possible.
There are several available libraries that provide some syntactic sugar for async operations in Javascript. For example:
https://github.com/caolan/async
https://github.com/coolaj86/futures
...however I don't think anything provides the synchronous syntax you are looking for - there is always a callback involved, because of the way JavaScript works in the browser.