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.
Related
I'm trying to make a request to a route which makes a query to an API and if the API has the data, the response is to render another website with the API data. But if the data is not ready yet, since it is still processing, the route returns a string "not finished yet".
What I wish to do is: make a get request and if the response is "not finished yet" wait for 5 seconds and do the request again until the response is the data. After it, the script would open the window with the new page with the data loaded.
Here is what I have already made:
job_id = document.querySelector("#job_id").getAttribute("value")
code = document.querySelector("#code").getAttribute("value")
var myRequest = new XMLHttpRequest();
myRequest.open('GET', `http://127.0.0.1:5000/status/${job_id}/${code}`);
myRequest.onreadystatechange = function () {
if (myRequest.readyState === 4 && myRequest.responseText != 'not finished yet') {
window.location = `http://127.0.0.1:5000/status/${job_id}/${code}`
}
};
If anyone knows if it works or knows a better way to deal with that, I'd appreciate your help.
Thanks in advance.
Solution:
After some hours, I finnaly found a way to handle it. Still don't know if it is the best way.
function search() {
job_id = document.querySelector("#job_id").getAttribute("value")
code = document.querySelector("#code").getAttribute("value")
var myRequest = new XMLHttpRequest();
myRequest.open('GET', `http://127.0.0.1:5000/status/${job_id}/${code}`);
myRequest.send();
myRequest.onreadystatechange = function () {
if (myRequest.readyState === 4 && myRequest.responseText === 'not finished yet') { setTimeout(function () {search();}, 5000)
}
else if(myRequest.readyState === 4 && myRequest.responseText != 'not finished yet')
{ window.location = `http://127.0.0.1:5000/status/${job_id}/${code}`}}
}
search()
I use var option = {}; as a global object to handle OOP (object oriented programming).
What you want to do is when you need to define something give it a prefix for the function and an identifier so you can avoid conflicts.
You posted some code so at 1 reputation and decent formatting you're doing a lot better than most starting at 1, kudos. Let's say you're working with a job ID of 79. So you'll want to define the following:
option.job_79 = 1;
Now I assigned the sub-object a 1 as a status, it's initialized. Since the option object is global scope you can have another call to your ajax() function and without it knowing that another ajax() function is already running you simply check for the typeof option.job_79 instead!
Some recommendations:
If you're enthusiastic about programming you'll eventually want to merge all your AJAX functions in to one single well refined function, it'll not only greatly simplify your code the up-front cost will save you and the earlier the better (though the more you'll have to refine it over time).
Also avoid the evils of frameworks and libraries. People make such a big deal about them but a few years later when you want to update you can't without spending days or weeks refactoring code. I've never had to refactor code using only pure JavaScript for any other reason other than my experience level, never because of a browser update. There are numerous other benefits that are hidden along that path and most people aren't aware of that.
My code is structured as follows:
IF (something) {
..stuff
..Asynchronous Function Call
}
ELSE (something) {
..stuff
..Asynchronous Function Call
}
..more stuff
Let's say the IF condition is met, the code executes 'stuff', then moves onto the Asynchronous Function Call. Will it simple do the call but get out of the IF statement and execute 'more stuff' in the mean time on the assumption of waiting for the Asynchronous Function Call to finish?
OR
Does it finish waiting for the Asynchronous Function Call to finish executing, then continue with 'more stuff' as a normal IF statement block would do.
In the prior case, any advice on how to ensure the Asynchronous Function Call finished before it exits the IF block?
** Note, I've included more stuff inside both Asynchronous Function Calls to ensure the calls are done before it moves on, but I feel this is really bad programming because if I had 50 ELIF's, I would have to copy paste that code 50 times as opposed to just putting it at the end of the IF statement.
Thank you very much for any help provided!
You can approach this easily and less painfully using JavaScript Promises. Have a look to the following links:
http://davidwalsh.name/write-javascript-promises
https://www.promisejs.org/
The basic idea of JavaScript Promises is to the use of asynchronous calls that can be executed in a certain order. Like this:
$.when(GET_PRODUCTS).then(
IF_SUCCESS DO THIS
ELSE DO THAT
).fail(
SHOW MESSAGE
CLEAN EVERYTHING BECAUSE SOMETHING WRONG HAPPENED
).done(
CLEAN EVERYTHING BECAUSE EVERYTHING WENT OKAY
)
With that, you can make code that will be more maintainable. It is not easy to grasp it at the beginning, but give it a try, will save you a lot of headaches!
Does it finish waiting for the Asynchronous Function Call to finish executing,
No, that isn't what "asynchronous" means. The while point is that it doesn't wait. The function will run and finish at some point in the future; the flow of execution continues to the next line immediately.
In regards to your given code, more stuff happens while the asynchronous function is happening. It doesn't wait for the asynchronous function to return a result.
Based on your tag of "node.js", I'm assuming your question is about asynchronous calls on the server. However, you can compare the behavior to a client-side AJAX call.
Say you have this:
var nav = document.getElementById('nav');
function async(params) {
var xhr = new XMLHttpRequest();
// set up your request
xhr.onreadystatechange = function() {
// some conditions, and then on success:
nav.style.color = 'black';
};
xhr.open('GET', 'resource.php'+params, true);
// send your request
}
if ( /* condition */ ) {
async( /* some parameter */ );
} else {
nav.style.color = 'red';
}
If you were to run the above code, either way, your #nav element's color will be set to red at first, but if the async request comes back with a successful response, your #nav element's color will be black. This is a very trivial and probably impractical example, but it is one that could be tested pretty easily to confirm that yes, async calls will happen asynchronously.
Like others of said you can use Promises, async.js, step.js, etc. To control flow. You can also use generators if you use latest version of node with --harmony enabled.
I promise to show you the right way. First off asynchronous if conditions are what you're looking for. Secondly you want a full example. You'll have to modify the URL of the AJAX request and set some server code to give you responses. I'll provide a baseline PHP file towards the end.
So effectively: what if my if condition takes too long for the JavaScript parser? JavaScript uses promises. I'm not going to go all-out crazy, my goal here is to provide a baseline. Towards the end of the script you'll notice either success or failure levels. This script requires two asynchronous if conditions. Additionally instead of being cheap/static/fragile I've kept the script element within the head element where it belongs. Lastly ensure you change the HTTP query and acknowledge it at the server, no need to produce redundant files. Browser compatibility is good except no support for IE11 however I've only encountered very specific use-cases to require this so if you're considering using this code for non-technical audience I would highly recommend reconsider your initial approach to the given problem.
<head>
<script defer="true" type="application/javascript">
//<![CDATA[
function request(method,url)
{
return new Promise(function(resolve, reject)
{
var req = new XMLHttpRequest();
req.open(method,url);
req.withCredentials = true;
req.onerror = function() {reject(Error('Network error.'));};
req.onload = function() {if (req.status == 200) {resolve(req.response);} else {reject(Error(new Object({'response':req.response,'status':req.statusText})));}};
req.send();
});
}
function aysn_if()
{
request('get','https://www.example.com/test.php?t=1').then(function(response)
{
console.log('Success, level one if!', response);
request('get','https://www.example.com/test.php?t=2').then(function(response)
{
console.log('Success, level two if!', response);
},
function(error)
{
console.error('Failed, second level.', error);
});
},
function(error)
{
console.error('Failed, first level.', error);
});
}
document.addEventListener('DOMContentLoaded', function (e) {aysn_if();},false);
//]]>
</script>
</head>
PHP
<?php
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Allow-Origin: '.((isset($_SERVER['HTTP_ORIGIN'])) ? $_SERVER['HTTP_ORIGIN'] : '*'));
//JUST for testing, don't send this stuff outside of test environments!
ksort($_SERVER);
print_r($_SERVER);
?>
You first option is what happens.
You don't have to copy/paste N times. Just put "more stuff" into a function, and pass that function to all your asynchronous callbacks. The callbacks can just call the "more stuff" function when they are done with their normal processing.
Is there any way to detect global AJAX calls (particularly responses) on a web page with generic JavaScript (not with frameworks)?
I've already reviewed the question "JavaScript detect an AJAX event", here on StackOverflow, and tried patching in the accepted answer's code into my application but it didn't work. I've never done anything with AJAX before either so, I don't know enough to modify it to work.
I don't need anything fancy, I just need to detect all (specific, actually, but I'd have to detect all first and go from there) AJAX responses and patch them into an IF statement for use. So, eventually, I'd like something like:
if (ajax.response == "certainResponseType"){
//Code
}
, for example.
Update:
It seems I should clarify that I'm not trying to send a request - I'm developing a content script and I need to be able to detect the web page's AJAX requests (not make my own), so I can execute a function when a response is detected.
Here's some code (tested by pasting into Chrome 31.0.1650.63's console) for catching and logging or otherwise processing ajax requests and their responses:
(function() {
var proxied = window.XMLHttpRequest.prototype.send;
window.XMLHttpRequest.prototype.send = function() {
console.log( arguments );
//Here is where you can add any code to process the request.
//If you want to pass the Ajax request object, pass the 'pointer' below
var pointer = this
var intervalId = window.setInterval(function(){
if(pointer.readyState != 4){
return;
}
console.log( pointer.responseText );
//Here is where you can add any code to process the response.
//If you want to pass the Ajax request object, pass the 'pointer' below
clearInterval(intervalId);
}, 1);//I found a delay of 1 to be sufficient, modify it as you need.
return proxied.apply(this, [].slice.call(arguments));
};
})();
This code solves the above issue with the accepted answer:
Note that it may not work if you use frameworks (like jQuery), because
they may override onreadystatechange after calling send (I think
jQuery does). Or they can override send method (but this is unlikely).
So it is a partial solution.
Because it does not rely on the 'onreadystatechange' callback being un-changed, but monitors the 'readyState' itself.
I adapted the answer from here: https://stackoverflow.com/a/7778218/1153227
Gives this a try. Detects Ajax responses, then I added a conditional using the XMLHttpRequest propoerties readyState & status to run function if response status = OK
var oldXHR = window.XMLHttpRequest;
function newXHR() {
var realXHR = new oldXHR();
realXHR.addEventListener("readystatechange", function() {
if(realXHR.readyState==4 && realXHR.status==200){
afterAjaxComplete() //run your code here
}
}, false);
return realXHR;
}
window.XMLHttpRequest = newXHR;
Modified from:
Monitor all JavaScript events in the browser console
This can be a bit tricky. How about this?
var _send = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function() {
/* Wrap onreadystaechange callback */
var callback = this.onreadystatechange;
this.onreadystatechange = function() {
if (this.readyState == 4) {
/* We are in response; do something,
like logging or anything you want */
}
callback.apply(this, arguments);
}
_send.apply(this, arguments);
}
I didn't test it, but it looks more or less fine.
Note that it may not work if you use frameworks (like jQuery), because they may override onreadystatechange after calling send (I think jQuery does). Or they can override send method (but this is unlikely). So it is a partial solution.
EDIT: Nowadays (the begining of 2018) this gets more complicated with the new fetch API. Global fetch function has to be overridden as well in a similar manner.
A modern (as of April 2021) answer to the question is to use PerformanceObserver which lets you observe both XMLHttpRequest requests and fetch() requests:
Detect fetch API request on web page in JavaScript
Detect ajax requests from raw HTML
<!-- Place this at the top of your page's <head>: -->
<script type="text/javascript">
var myRequestLog = []; // Using `var` (instead of `let` or `const`) so it creates an implicit property on the (global) `window` object so you can easily access this log from anywhere just by using `window.myRequestLog[...]`.
function onRequestsObserved( batch ) {
myRequestLog.push( ...batch.getEntries() );
}
var requestObserver = new PerformanceObserver( onRequestsObserved );
requestObserver.observe( { type: 'resource' /*, buffered: true */ } );
</script>
I use the above snippet in my pages to log requests so I can report them back to the mothership in my global window.addEventListenr('error', ... ) callback.
The batch.getEntries() function returns an array of DOM PerformanceResourceTiming objects (because we're only listening to type: 'resource', otherwise it would return an array of differently-typed objects).
Each PerformanceResourceTiming object has useful properties like:
The initiatorType property can be:
A HTML element name (tag name) if the request was caused by an element:
'link' - Request was from a <link> element in the page.
'script' - Request was to load a <script>.
'img' - Request was to load an <img /> element.
etc
'xmlhttprequest' - Request was caused by a XMLHttpRequest invocation.
'fetch' - Request was caused by a fetch() call.
name - The URI of the resource/request. (If there's a redirection I'm unsure if this is the original request URI or the final request URI).
startTime: Caution: this is actually the time since PerformanceObserver.observe() was called when the request was started.
duration: Caution: this is actually the time since PerformanceObserver.observe() was called when the request completed: it is not the duration of the request alone. To get the "real" duration you need to subtract startTime from duration.
transferSize: the number of bytes in the response.
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;
}
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.