Here is my sample code. (The reason I need to send synchronous requests is that actually I need to send several requests and each requests depends on the previous request's response. And the reason I need to set callbacks is that I want to show some spinner, so users knows the status of the script.)
var xmlhttp =new XMLHttpRequest();
xmlhttp.onreadystatechange=function() {
if (xmlhttp.readyState==2) {
document.getElementById("p2").style.color = "blue"; //statment 1
}
if (xmlhttp.readyState==4) {
document.getElementById("p2").style.color = "red"; //statment 2
}
}
xmlhttp.open("GET","ajax_info.txt",false);
xmlhttp.send();
balblabla(); // a time-cost function
Then I have 2 questions.
First, when will statement 1 and statement 2 actually get executed?
Is it guaranteed to get executed before balblabla()?
Second, Even if statement 1 and statement 2 get executed, seems browser won't actually change the displayed color until blablabla() is finished.
Is there a way to make color change got displayed before blablabla() finished? (assuming blablabla() takes a long time)
Thanks!
If the XHR is synchronous then the callbacks are executed before .send() returns. In other words before blablabla().
Browser DOM updates are asynchronous. Or rather, the redraws are asynchronous (DOM update/reflow can at times be synchronous but it won't draw to screen, just update data structures).
So, even if you insist on not learning how to write asynchronous programs by using synchronous XMLHttpRequest you can't draw anything synchronously. You'd be better off writing everything asynchronous.
note: To be clear, redraw only happens when the javascript interpreter have nothing else to run. In other words, the browser window will only be updated after blablabla() completes running.
If blablabla() takes a long time to execute, you can break up the loop using setTimeout() to make it asynchronous. Alternatively you can try using webworkers.
Related
Let's say I have a function that does a standard AJAX request:
function doXHR() {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts');
xhr.send();
xhr.onreadystatechange = () => {
console.log('ready state change');
};
}
doXHR();
console.log('done');
This code will cause the browser to start an AJAX request, but at what point in the function does the request actually start? According to this post: https://blog.raananweber.com/2015/06/17/no-there-are-no-race-conditions-in-javascript/
[Calling xhr.onreadystate() after send()] is possible, because the HTTP request is only executed after the current scope has ended its tasks. This enables the programmer to set the callbacks at any position he wishes. As JavaScript is single-threaded, the request can only be sent when this one single thread is free to run new tasks. The HTTP request is added to the list of tasks to be executed, that is managed by the engine.
But when I add a breakpoint in devtools right after the send call:
I do get a network request, albeit in a pending state:
At the breakpoint, the XHR's readystate is 1, which is XMLHttpRequest.OPENED. According to MDN's documentation, XMLHttpRequest.OPENED means that xhr.open() has been called: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/readyState
But if I comment out xhr.send() so that only .open() is called:
Then I get no pending network request:
Thinking that perhaps I need the function scope to end in order for the request to actually be sent out, I moved the breakpoint to after the function call (and modified the code a bit to see the XHR readyState):
But I still get the pending network request:
It seems like the debugger not only freezes code execution, but also any network requests. This makes sense, as you don't want network requests to complete while you're on a breakpoint, but this also makes it impossible to tell when the network request is actually initiated.
My question is, at what point does the request actually get sent out? Does calling xhr.send() merely set up the request, but requires that the function scope end before the request is initiated? Does xhr.send() immediately initiate the request before the function scope ends? Or is there something else going on here?
send immediately initiates the request. This is at least hinted at by MDN's documentation for send.
The author of the blog you link to is correct that there are no race conditions per-se, but that does not keep you from having things happen out-of-order. An example of this would be if you load multiple <script> tags in with the async=true set on them. In this case, if one script depends on the other, you could end up in a situation where you have an unpredictable sequence of events (which is very similar to a race condition) because two asynchronous events finish at different times.
It is true that you can set onreadystatechange after calling send because even if the request request failed immediately, the event won't get dispatched until the function scope completes. The delay here is not in the dispatching of the network request, but in the dispatching of the event to say that the network request completed.
It is important to note, that networking itself is not handled in JavaScript, but rather by the browser implementation, which is native code, and could be multi-threaded (although I do not know if it is). This means that the browser is perfectly capable of handling network tasks while your javascript is running.
I have a script I'm using to schedule resources using an API and a list in CSV format. The script currently loops through the CSV and fires off a call to a function that has the API calls in it. The AJAX calls are nested (Create a Reservation->Take the reservation number and add a resource->Validate the reservation->Submit the reservation). The problem is that after the original AJAX call, it seems to hang until all of the AJAX calls have completed. There doesn't seem to be any asynchronicity going on.
for(line in CSV)
{
makeAPICalls(line)
}
function makeAPICalls(line)
{
$.ajax("Create Reservation").then(function(){
$.ajax("Add Resource to Reservation").then(function(){
$.ajax("Validate Reservation").then(function(){
$.ajax("Confirm Reservation")
})
})
})
}
The first API call ("Create Reservation") completes, and then waits for all of the other lines in the CSV to make that call, then they ALL move on to the next step ("Add Resource to Reservation"). I was wondering if the system was just moving too quickly, so there wasn't a chance for everything to get "out of sync", so I added a delay before makeAPICalls(), but it still waited. Once the CSV loop finished, all the AJAX calls moves from ("Create Reservation") to the then("Add Resource to Reservation").
Is this as expected? Ideally I'd like each call to makeAPICalls() to finish as quickly as possible, with no regards for any other calls (which I kind of thought was what async was all about, but it doesn't seem to be happening here.
This is happening because you are chaining the requests. If your requests are not dependent on each other, you can call them without using .then().
The behaviour is quite correct. I don't know how you are putting in the delay but it probably won't help since JavaScript is single-threaded.
If you want all the steps to complete for a specific csv line you could have your function process the list one-by-one. You could even have the last step call back into the function with the next index to process.
I want to make sure the result is shown to user, so I make synchronous AJAX call. It's quite simple to display a 'Loading' indicator with asynchronous AJAX (the examples are everywhere), but when I use synchronous AJAX call with XMLHttpRequest, the loading indicator GIF image doesn't show up at all.
Some said that it's impossible to show indicator when doing a synchronous call (block until having response from server). But I just want to ask to see whether there's a way to do it.
It's "impossible" because Javascript is single-threaded, and the synchronous call blocks updates to the UI.
However, you may be able to display an animated 'loading' graphic before launching the synchronous AJAX call, and removing it upon success or failure. I believe most browsers will be able to continue rendering the animated gif even while technically blocked for the synchronous call.
It is possible, see the top answer here:
How to show waiting message during sync ajax call in browser
The solution is to show your loading message, hand control back to the browser, and then lock everything up with your synchronous remote call. One way to do this is to use setTimeout with a delay of zero
I have written a JavaScript function that asynchronously calls a web service using XmlHttpRequest. I have been asked to make this function finish its work before the page is rendered.
I thought I could make the AJAX request synchronous but I don't want this to make the page hang too long - I'd like to abort the request after, say, 1 second if a response isn't received.
Is it possible to abort a synchronous XmlHttpRequest?
You can't:
http://www.hunlock.com/blogs/Snippets:_Synchronous_AJAX sais:
"Synchronous AJAX (Really SJAX -- Synchronous Javascript and XML) is modal which means that javascript will stop processing your program until a result has been obtained from the server. While the request is processing, the browser is effectively frozen. The browser treats the call like an alert box or a prompt box only it doesn't wait for input from the user, but on input by the remote server"
Once the browser runs the sync request, it will wait until it will get a response.
First of all, synchronous AJAX calls are evil because they are blocking the whole JavaScript browser engine (which you are aware of).
Is simply doing the call asynchronously and discarding the result if it arrives later than after a second is not enough? If you really want to wait for the result you can still use setTimeout() (jQuery for convenience, not required):
var result;
var xhr = $.ajax('/url', function(response) {
result = response;
});
setTimeout(function() {
if(result) {
//AJAX call done within a second, proceed with rendering
} else {
//One second passed, no result yet, discard it and move on
xhr.abort();
}
}, 1000);
With this approach you are not blocking the browser while still you don't have to wait for the AJAX call.
XMLHttpRequest support abort method, you can get more details about it here: http://www.w3.org/TR/XMLHttpRequest/#the-abort-method
But you need to check how many browserы support it. For example, abort was introduced in Windows Internet Explorer 7 and above.
Before or after calling send() method of XMLHttpRequest object you can setup a timer for n-seconds delay which will interrupt an asynchronous operation which is in progress.
It is possible in IE.Use the timeout property.
the code below worked for me
xmlhttp.open("method","url",false);
xmlhttp.timeout="time in ms";
xmlhttp.ontimeout=function(){};
xmlhttp.send();
I use a XMLHttpRequest on a signup form to see if the username they entered has been taken already. The script runs fine, but I notice that there's some time delay when making the request - the webpage is frozen for a second, after sending the request.
Is there a way to have the request "in the background" and not cause any lag on the front end? I like Twitter's implementation: it shows an spinning hourglass icon while it's searching the database. How do I get something similar?
You want to use asynchronous XHR -- currently you're performing a synchronous request, so the page has to freeze until the load has complete. Asynchronous XHR calls a callbck function you provide with the load status updates.
If memory serves you just need to do
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) loadFinished();
}
xhr.open(requestType, url, true);
Where true makes the request asynchronous.
You need to specify that XMLHttpRequest operate asynchronously--in the background. Then you can provide a callback function so users can continue browsing until the operation is complete.
Many sites simply show an animated GIF while waiting for the operation to return, which probably gives the user the impression that you're looking for since it looks like something is happening. You can design and download one of those AJAX-spinning indicators easily at http://www.ajaxload.info/.