Force Javascript function call to wait until previous one is finished - javascript

I have a simple Javascript function:
makeRequest();
It does a bunch of stuff and places a bunch of content into the DOM.
I make a few calls like so:
makeRequest('food');
makeRequest('shopping');
However, they both fire so quickly that they are stepping on each other's toes. Ultimately I need it to have the functionality of.
makeRequest('food');
wait....
makeRequest('shopping'); only if makeRequest('food') has finished
Thoughts on getting these to execute only one at a time?
Thanks!

If these functions actually do an AJAX request, you are better keeping them asynchronous. You can make a synchronous AJAX request but it will stop the browser from responding and lead to bad user experience.
If what you require if that these AJAX requests are made one after the other because they depend on each other, you should investigate your function to see if it provides a callback mechanism.
makeRequest('food', function()
{
// called when food request is done
makeRequest('shopping');
});
Using jQuery, it looks something like that
$.get("/food", function(food)
{
// do something with food
$.get("/shopping", function(shopping)
{
// do something with shopping
});
});

I would recommend that you simply write them asynchronously--for example, call makeRequest('shopping'); from the AJAX completion handler of the first call.
If you do not want to write your code asynchronously, see Javascript Strands

I suppose that you have a callback method that takes care of the response for the request? Once it has done that, let it make the next request.
Declare an array for the queue, and a flag to keep track of the status:
var queue = [], requestRunning = false;
In the makeRequest method:
if (requestRunning) {
queue.push(requestParameter);
} else {
requestRunning = true;
// do the request
}
In the callback method, after taking care of the response:
if (queue.length > 0) {
var requestParameter = queue.splice(0,1)[0];
// do the request
} else {
requestRunning = false;
}

Related

Hold on callback function execution until promise is completed

I have some third party library whose events I'm listening. I get a chance to modify data which that library is going to append in the UI. It is all fine until that data modification is synchronous. As soon as I involve Ajax callbacks/promises, this fails to work. Let me put an example to show case the problem.
Below is how I'm listening to a event:-
d.on('gotResults', function (data) {
// If alter data directly it works fine.
data.title = 'newTitle';
// Above code alters the text correctly.
//I want some properties to be grabbed from elsewhere so I make an Ajax call.
$.ajax('http://someurl...', {data.id}, function (res) {
data.someProperty = res.thatProperty;
});
// Above code doesn't wait for ajax call to complete, it just go away and
renders page without data change.
// Yes I tried promises but doesn't help
return fetch('http://someurl...').then(function (data) {
data.someProperty = res.thatProperty;
return true;
});
// Above code also triggers the url and gets away. Doesn't wait for then to complete.
});
I cannot change/alter the third party library. All I have is to listen to event and alter that data.
Any better solutions. Nope. I can't use async/wait, generators, because I want to have it supported for ES5 browsers.
You cannot make a synchronous function wait for an asynchronous response, it's simply not possible by definition. Your options pretty much are:
BAD IDEA: Make a synchronous AJAX request. Again: BAD IDEA. Not only will this block the entire browser, it is also a deprecated practice and should not be used in new code, or indeed ever.
Fetch the asynchronous data first and store it locally, so it's available synchronously when needed. That obviously only works if you have an idea what data you'll be needing ahead of time.
Alter the 3rd party library to add support for asynchronous callbacks, or request that of the vendor.
Find some hackaround where you'll probably let the library work with incomplete data first and then update it when the asynchronous data is available. That obviously depends a lot on the specifics of that library and the task being done.
Does the gotResults callback function really need to return anything else than true? If not, then you could just write regular asynchronous code without this library knowing about it. Let me explain myself by rewriting your pseudocode:
d.on('gotResults', function (data) {
// If alter data directly it works fine.
data.title = 'newTitle';
// Above code alters the text correctly.
//I want some properties to be grabbed from elsewhere so I make an Ajax call.
$.ajax('http://someurl...', {data.id}, function (res) {
data.someProperty = res.thatProperty;
// Above code doesn't wait for ajax call to complete, it just go away and
// EDIT: now it should render properly
renders page without data change.
// Yes I tried promises but doesn't help
return fetch('http://someurl...');
// Above code also triggers the url and gets away. Doesn't wait for then to complete.
}).then(function (data) {
data.someProperty = res.thatProperty;
// maybe render again here?
}).catch(function(err) {
handleError(err); // handle errors so the don't disappear silently
});
return true; // this line runs before any of the above asynchronous code but do we care?
});

How to work with 2 XMLHttpRequest one dependent on another?

I am working on a project where I have got 2 XMLHttpRequest() objects, say A and B.
What I want to accomplish is when A finish fetching a list of data items, B will be triggered to fetch some more items based on the previous data items fetch by A.
Currently my problem is that the two objects are working independent of one another.
My code is below:
var A = new XMLHttpRequest();
var B = new XMLHttpRequest();
A.open("GET", directory, true);
A.onreadystatechange = function () {
if (A.readyState === 4) {
if (A.status === 200 || A.status == 0) {
//does... something
}
}
}
A.send(null);
while(true){
B.open("GET", another_directory, false);
B.overrideMimeType("application/document");
B.send(null);
if (B.status == "404")
continue;
//does... something else
}
This code is not working because I find evertime B proceed before A can complete. I basically don't know which event to use.
How can I accomplish my objective?
What events can I use so that I can sync processing B right after finishing with A?
Ok, so let's start with your code. I've added a few comments to it, so now you can understand the source of the problem:
var A = new XMLHttpRequest(); //You create an XMLHttpRequest object
var B = new XMLHttpRequest(); //And an another
A.open("GET", directory, true);
/* Now you open a GET request to DIRECTORY, with async TRUE. The third parameter can
make a request sync or async, but sync is not recommended as described below. */
A.onreadystatechange = function () {
if (A.readyState === 4) {
if (A.status === 200 || A.status == 0) {
/* So you registered an event listener. It runs when the readyState changes.
You can use it to detect if the request is finished or not. If the readyState is
4, then the request is finished, if the status code is 200, then the response is
OK. Here you can do everythin you want after the request. */
}
}
}
A.send(null); //Now you send the request. When it finishes, the event handler will
// do the processing, but the execution won't stop here, it immediately goes to the
// next function
while(true){ // Infinite loop
B.open("GET", another_directory, false); //Open request B to ANOTHER_DIRECTORY,
// but now, request B will be synchronous
B.overrideMimeType("application/document"); // Configure mime type
B.send(null); // Send the request
if (B.status == "404")
continue;
// If it's not found, then go to the next iteration
// and do something else
}
I hope that now you can see the source of the problem. When you run this script, then your start an async request and then immediately start the next one. Now you can choose from 2 ways.
Run next request from callback (recommended)
It's the better way. So start your first (async) request and in the event listener (where you do the processing) you can start the next request. I've made a commented example here: http://jsfiddle.net/5pt6j1mo/1/
(You can do it without arrays - it was just an example)
If you use this way then the GUI won't freeze until you are waiting for response. Everything will be responsible so you can interact with the page, you can create cancel button, etc.
Synchronous AJAX (not recommended)
I don't recommend it because "Synchronous XMLHttpRequest on the main thread is deprecated" in Chrome, but if you really want to then you can try to use this solution. So an XMLHttpRequest's open function has 3 arguments:
METHOD: which HTTP methid to use
URL: which URL to request
ASYNC: Asynchronous request? If false then it will be synchronous wich means that after you call .send(), it will pause execution until the response comes back.
So if you set the third parameter to FALSE then you can easily do it... but you shouldn't!
Here is an alternative solution, either use the fetch API or promisify native XHR and this problem becomes much simpler:
fetch(directory).then(function(response){
// some processing
return fetch(another_directory); // can change content type too, see the mdn docs
}).then(function(responseTwo){
// all processing is done
}).catch(function(err){
// handle errors from all the steps above at once
});
This is just as native as XHR, and is much much simpler to manage with promises.
(After lengthy edit) I'd recommend strongly that you take the time to understand the nature of asynchronous calls within JavaScript. Here's a bit of recommended reading.Asynchronous Programming in JavaScript I think that is simple enough to understand what is going on. Note: Stop reading at "Enter Mobl".
In JavaScript when you call a function, the system places that function into a 'queue' with an implicit instruction to go ahead and run it as soon as you can. It does that for each and every function call. In your case you are telling the system to run A, then run B. A goes in the queue, B goes in the queue. They are submitted as individual functions. B happens to run first.
For normal functions, if you want to control the sequence, you can nest the A function call within the B function call. But oops. You are using XMLHttpRequest, so that limits your ability to customize the functions. Read on. Check out Ajax Patterns on the subject Look at the paragraph for "Asynchronous Calls". Look at your code...
A.onreadystatechange = function () {
if (A.readyState === 4) {
if (A.status === 200 || A.status == 0) {
//does... something
(RUN ALL THE B.methods right here...)
}
}
}
I think that will get you to your destination, assuming you want a no jQuery solution.
For the person who just wants a functioning system, and doesn't want to understand the language better, here is a jquery solution... Note how the B function call is nested within the A function call. Do note that the order of this nesting is based on the presence of the jQuery success tag. If not using jQuery, you will manually have to nest the functions as appropriate.
var special_value;
$("button").click(function(){
$.ajax({url: "demo_testA.html",
type: 'GET',
success: function(resultA){
special_value = resultA;
$.ajax({url: "demo_testB.html",
type: 'GET',
data: special_value,
success: function(resultB){
$("#div1").html(resultB);
}});
});
});
I will say, it would be much easier to help you help yourself with the use of better communications. If you don't like something, then so state. If you don't understand something ask for more clarification or edit your problem statement. Feedback is a good thing.

sequential functioncalls

I am communicating with a servlet that changes and saves an external file. Since this takes some time, I need some of my javascript functioncalls to happen sequentially so that the actions of one function don't interfear the actions of another function.
To do this, I wrote a 'sequential' function that takes another function that can only be called when the busyflag is set to false (i.e. when no other functioncall is handled at the same time). This is my code:
var busy = false;
function sequential(action) {
while(busy)
setTimeout(function(){sequential(action);}, 10);
busy = true;
action();
setTimeout(function(){busy = false;}, 100);
}
function test1() {sequential(function() {alert("test1");});}
function test2() {sequential(function() {alert("test2");});}
And this is the example on jsFiddle. For some reason this code this code keeps looping on the second call (when a functioncall has to wait).
while(busy)
setTimeout(function(){sequential(action);}, 10);
setTimeout does not block, it returns immediately and allows the loop to continue. Javascript is single threaded, so this loop just keeps running and prevents any other code from executing, meaning busy will never get set to false to exit the loop.
Assuming these things you are waiting on are ajax calls, you will likely want to use some sort of queue and then in the callback of the ajax call, run the next request.
I presume your javascript is making ajax calls to your server.
If you need the different calls to run one after the other, then you should get your javascript code to set up hooks to wait until it gets results back from one call before making the next request.
I recommend using a javascript toolkit like jQuery for these purposes. It makes problems like this much easier to solve. Every ajax method in jQuery accepts at least a callback that will be called when the query is complete. For jQuery.ajax() you can go
$.ajax(...).done(function() {
// This part will be run when the request is complete
});
And for .load():
$("#my_element").load(url,data,function() {
// This part will be run when the request is complete
});
I first implemented a solution like suggested by James Montagne, but after some searchin I found out that you can use the onreadystatechange property of the XMLHttprequest to set the busy-flag to true.
This code works like expected:
function sequential(action) {
if (busy) setTimeout(function(){sequential(action);}, 20);
else action();
}
function send(message){
busy = true;
var request = new XMLHttpRequest();
request.open("POST", "owlapi", true);
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
request.send(message);
request.onreadystatechange = function() {busy = false;};
}

Waiting on multiple asynchronous calls to complete before continuing

So, I have a page that loads and through jquery.get makes several requests to populate drop downs with their values.
$(function() {
LoadCategories($('#Category'));
LoadPositions($('#Position'));
LoadDepartments($('#Department'));
LoadContact();
};
It then calls LoadContact(); Which does another call, and when it returns it populates all the fields on the form. The problem is that often, the dropdowns aren't all populated, and thus, it can't set them to the correct value.
What I need to be able to do, is somehow have LoadContact only execute once the other methods are complete and callbacks done executing.
But, I don't want to have to put a bunch of flags in the end of the drop down population callbacks, that I then check, and have to have a recursive setTimeout call checking, prior to calling LoadContact();
Is there something in jQuery that allows me to say, "Execute this, when all of these are done."?
More Info
I am thinking something along these lines
$().executeAfter(
function () { // When these are done
LoadCategories($('#Category'));
LoadPositions($('#Position'));
LoadDepartments($('#Department'));
},
LoadContact // Do this
);
...it would need to keep track of the ajax calls that happen during the execution of the methods, and when they are all complete, call LoadContact;
If I knew how to intercept ajax that are being made in that function, I could probably write a jQuery extension to do this.
My Solution
;(function($) {
$.fn.executeAfter = function(methods, callback) {
var stack = [];
var trackAjaxSend = function(event, XMLHttpRequest, ajaxOptions) {
var url = ajaxOptions.url;
stack.push(url);
}
var trackAjaxComplete = function(event, XMLHttpRequest, ajaxOptions) {
var url = ajaxOptions.url;
var index = jQuery.inArray(url, stack);
if (index >= 0) {
stack.splice(index, 1);
}
if (stack.length == 0) {
callback();
$this.unbind("ajaxComplete");
}
}
var $this = $(this);
$this.ajaxSend(trackAjaxSend)
$this.ajaxComplete(trackAjaxComplete)
methods();
$this.unbind("ajaxSend");
};
})(jQuery);
This binds to the ajaxSend event while the methods are being called and keeps a list of urls (need a better unique id though) that are called. It then unbinds from ajaxSend so only the requests we care about are tracked. It also binds to ajaxComplete and removes items from the stack as they return. When the stack reaches zero, it executes our callback, and unbinds the ajaxComplete event.
You can use .ajaxStop() like this:
$(function() {
$(document).ajaxStop(function() {
$(this).unbind("ajaxStop"); //prevent running again when other calls finish
LoadContact();
});
LoadCategories($('#Category'));
LoadPositions($('#Position'));
LoadDepartments($('#Department'));
});
This will run when all current requests are finished then unbind itself so it doesn't run if future ajax calls in the page execute. Also, make sure to put it before your ajax calls, so it gets bound early enough, it's more important with .ajaxStart(), but best practice to do it with both.
Expanding on Tom Lianza's answer, $.when() is now a much better way to accomplish this than using .ajaxStop().
The only caveat is that you need to be sure the asynchronous methods you need to wait on return a Deferred object. Luckily jQuery ajax calls already do this by default. So to implement the scenario from the question, the methods that need to be waited on would look something like this:
function LoadCategories(argument){
var deferred = $.ajax({
// ajax setup
}).then(function(response){
// optional callback to handle this response
});
return deferred;
}
Then to call LoadContact() after all three ajax calls have returned and optionally executed their own individual callbacks:
// setting variables to emphasize that the functions must return deferred objects
var deferred1 = LoadCategories($('#Category'));
var deferred2 = LoadPositions($('#Position'));
var deferred3 = LoadDepartments($('#Department'));
$.when(deferred1, deferred2, deferred3).then(LoadContact);
If you're on Jquery 1.5 or later, I suspect the Deferred object is your best bet:
http://api.jquery.com/category/deferred-object/
The helper method, when, is also quite nice:
http://api.jquery.com/jQuery.when/
But, I don't want to have to put a bunch of flags in the end of the drop down population callbacks, that I then check, and have to have a recursive setTimeout call checking, prior to calling LoadContact();
No need for setTimeout. You just check in each callback that all three lists are populated (or better setup a counter, increase it in each callback and wait till it's equal to 3) and then call LoadContact from callback. Seems pretty easy to me.
ajaxStop approach might work to, I'm just not very familiar with it.

Is it possible to get a browser window to update while it is in a Javascript loop?

I have an Ajax call that currently needs to be synchronous. However, while this Ajax call is executing, the browser interface freezes, until the call returns. In cases of timeout, this can freeze the browser for a significant period of time.
Is there any way to get the browser (any browser) to refresh the user interface, but not execute any Javascript? Ideally it would be some command like window.update(), which would let the user interface thread refresh.
If this would be possible, then I could replace the synchronous AJAX call with something like:
obj = do_async_ajax_call();
while (!obj.hasReturned()) {
window.update();
}
// synchronous call can resume
The reason that I can't use setTimeout, or resume a function in the callback, is that the execution flow cannot be interrupted: (there are far too many state variables that all depend on each other, and the long_function() flow would otherwise have to be resumed somehow):
function long_function() {
// lots of code, reads/writes variable 'a', 'b', ...
if (sync_call_is_true()) {
// lots of code, reads/writes variable 'a', 'b', ...
} else {
// lots of code, reads/writes variable 'a', 'b', ...
}
// lots of code, reads/writes variable 'a', 'b', ...
return calculated_value;
}
You need to replace your synchronous request with an asynchronous request and use a callback. An oversimplified example would be:
obj = do_async_ajax_call(function (data, success)
{
if (success)
{
// continue...
}
});
function do_async_ajax_call(callback)
{
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://mysite.com", true);
xhr.onreadystatechange = function ()
{
if (xhr.readyState == 4 && xhr.status == 200)
callback(xhr.responseXML, true);
else if (xhr.readyState == 4)
callback(null, false);
}
xhr.send();
}
This way you're passing an anonymous function as a parameter to the ajax requesting function. When the ajax is complete, the function that was passed is called with the responseXML passed to it. In the meantime, the browser has been free to do it's usual thing until the call completes. From here, the rest of your code continues.
Take the rest of the call and put it in the callback that is called when the result comes back. I seriously doubt that this would be completely impossible for you to do. Any logic you need to put in the call can be duplicated in the callback
asynchronous ajax fetch then settimeout and do the processing work in chunks (triggered by the callback)
JavaScript is single-thread. So by definition, you cannot update UI while you are in a tide loop. However, starting from Firefox 3.5 there's added support for multi-threaded JavaScripts called web workers. Web workers can't affect UI of the page, but they will not block the updates of the UI either. We workers are also supported by Chrome and Safari.
Problem is, that even if you move your AJAX call into background thread and wait of execution to complete on it, users will be able to press buttons and change values on your UI (and as far as I understand, that's what you are trying to avoid). The only thing I can suggest to prevent users for causing any changes is a spinner that will block the entire UI and will not allow any interaction with the page until the web-call returns.

Categories