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.
Related
I'm making calls to the OpenWeatherMap API to get a weather forecast JSON object. I used 3 different javascript methods that are called when someone enters a zipcode in the zipweather html id element and presses submit or enter, calling zipWeather() and basically pasting the zipcode to the end of the api address which then sends back the data on that zipcode.
They all work fine. They all get a city name and temperature that is converted into Fahrenheit.
They all use a callback in the error handler to the function itself in case of failure. The first one uses a 5 second timeout callback.
onreadystatechange method:
function zipWeather() {
if (window.XMLHttpRequest) {
var xhr = new XMLHttpRequest();
}
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
//responseText code
}
// else ifs for readystate 3 2 and 1 gives a console log
else {
console.log("request failed, retrying in 5 seconds...");
window.setTimeout(zipWeather, 5000);
return;
}
}
xhr.open("GET", "http://api.openweathermap.org/data/2.5/weather?q=" + document.getElementById("zipweather").value,
true);
xhr.send();
event listeners instead of onreadystatechange:
xhr.addEventListener("load", function() {
//responseText code
}, false)
xhr.addEventListener("error", function(err) {
console.log(err);
if (weatherData.retryCount <= weatherData.retryMax) {
weatherData.retryCount++;
console.log(weatherData.retryCount);
console.log(err);
zipWeather();
return;
}
else {
return;
}
and of course jquery:
function zipWeather() {
$.ajax({
type: 'GET',
url: 'http://api.openweathermap.org/data/2.5/weather?q=' + $("#zipweather").val(),
data: {},
dataType: 'json',
success: function(data) {
console.log(data.name);
$('#weather').html(data.name).css({'color': 'blue', 'text-shadow': '1px 0.5px lightblue'});
//change from kelvin
var localTempF = Math.round((data.main.temp - 273.15) * 9/5 + 32);
$('#localtemp').html(localTempF + weatherData.localUnits);
weatherData.localTemperatureValue = localTempF;
},
timeout: 3000,
retryCount: 0,
retryMax: 5,
error: function(jqXHR, textStatus, errorThrown) {
this.retryCount++;
if (this.retryCount <= this.retryMax) {
//try again
$.ajax(this);
return;
}
return;
}
The last two methods use a retryCount and retryMax variable trick I found in What's the best way to retry an AJAX request on failure using jQuery? so it doesn't keep calling the API if its down.
Finally, questions:
Are all of these methods virtually identical in terms of performance? Is there a potential catastrophic bug lurking in any method as written?
Is it most proper to use a callback in the error handler to the same function when using AJAX?
Are javascript code standards moving away from using onreadystatechange or event handlers and towards jquery $.ajax and $.get functions?
Thanks everyone. Sorry it was so long!
You can probably answer the performance question yourself with performance testing tools. I prefer event listeners because the code reads more cleanly. Bug-wise, I would categorize the lack of a way for the first method to break out of a callback loop if the service is down as a serious bug. It could cause performance degradation if the service is down, that is something to check with performance testing.
I don't know whether there is a convention to re-call the method in the error handler, but it seems like an OK way to handle it as long as you have a loop breakout. If the retry max is reached, you might want to alert the user and prompt them that the service call will be attempted again after some arbitrary period. See the way Gmail handles connection interruptions for an example of this behavior.
As for jQuery v. not jQuery, it depends on your use case. In a lightweight web page where you are performing minimal JavaScript coding, you might find jQuery to be overkill in terms of the size of the library. On the other hand, jQuery is popular because it papers over browser incompatibilities, letting you, the coder, focus on application functionality. If you load it from a popular CDN, many users may already have it in cache, so load time would not be a factor. As for what people use, jQuery is popular, but beyond that I don't know whether any statistics exist that break down the relative popularity of either method.
Answers to your three questions:
1. Performance
Since the way to send the request in JavaScript doesn't affect the actual network loading performance at all, it simply doesn't matter. Also, there's almost no client side performance difference between the three.
2. Callback when failing
You handle that actually very nicely and elegant. Don't worry about a method unless its slow or doesn't work :D
3. Which one?
That's totally up to you! If you want to do jQuery, do it. If you feel like doing plain JavaScript with event listeners, do that.
Hope it helps, if you have any questions, feel free to ask :)
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.
First, I've looked at related SO questions, and didn't find much in the way of a suitable answer, so here goes:
I've been working on an HTML/Javascript page that acts as a UI to a back-end server. I made some pretty good strides in completing it, all while using synchronous calls in AJAX (aka var xmlhttp = new XMLHttpRequest(); xmlhttp.open(type, action, false);), but have now come to find out that Mozilla apparently doesn't like synchronous requests, and has therefore deprecated some much-needed functionality from them.
To quote https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest:
Note: Starting with Gecko 11.0 (Firefox 11.0 / Thunderbird 11.0 / SeaMonkey 2.8), as well as WebKit build 528, these browsers no longer let you use the responseType attribute when performing synchronous requests. Attempting to do so throws an NS_ERROR_DOM_INVALID_ACCESS_ERR exception. This change has been proposed to the W3C for standardization.
So that's great. I'm about to need to change the response type conditionally, but it won't work. It is now my intention to wrap an AJAX asynchronous request in something that will simulate synchronicity.
The following is a generic "make web request" function that my code uses, that I've started adapting to work for my purposes. Unfortunately, it isn't working quite like I'd hoped.
var webResponse = null;
function webCall(action, type, xmlBodyString) {
console.log("In webCall with " + type + ": " + action);
webResponse = null;
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function()
{
if (xmlhttp.readyState == 4)
{
if (xmlhttp.status == 200) {
webResponse = xmlhttp.responseXML;
} else {
var statusTxt = xmlhttp.statusText;
if (statusTxt == null || statusTxt.length == 0) {
statusTxt = "An Unknown Error Occurred";
}
throw "ERROR " + xmlhttp.status + ":" + statusTxt;
}
}
}
xmlhttp.open(type, action, true);
if (xmlBodyString == null) {
xmlhttp.send();
} else {
xmlhttp.setRequestHeader("Content-Type", "text/xml");
xmlhttp.send(xmlBodyString);
}
for (var i = 0; i < 20; i++) {
if (webResponse != null) {
break;
}
window.setTimeout(nop, 250);
}
if (webResponse == null) {
throw "Waited 5 seconds for a response, and didn't get one.";
}
console.log("Responding with " + webResponse);
return webResponse;
}
function nop() {
}
So, I thought this was pretty straight-forward. Create a global variable (in retrospect, it probably doesn't even have to be global, but for now, w/e), set up the onreadystatechange to assign a value to it once it's ready, make my asynchronous request, wait a maximum of 5 seconds for the global variable to be not null, and then either return it, or throw an error.
The problem is that my code here doesn't actually wait 5 seconds. Instead, it immediately exits, claiming it waited 5 seconds before doing so.
I made a fiddle, for what it's worth. It doesn't work in there either.
http://jsfiddle.net/Z29M5/
Any assistance is greatly appreciated.
You can't do it. Stick to asynchronous requests. Callback hell sucks, but that's what you get in event-driven systems with no language support.
There is simply no way to simulate synchronous code in plain JavaScript in browsers at the moment.
If you could severely limit your supported set of browsers (pretty much just Firefox at the moment AFAIK) you can get synchronous-looking code by using generators.
There are also languages that compile into JS and support synchronous-looking code. One example I can think of (from a few years ago) is this: https://github.com/maxtaco/tamejs
Firstly, for all the pain, using asynchronous code asynchronously is the way to go. It takes a different approach, that's all.
Secondly, for your specific question, this is what your 'delay' loop is doing:
For twenty iterations
if we've had a response, break
set a timeout for 250ms
go round again
(The entire for loop completes all 20 iterations immediately. You won't have a response)
.
.
.
after 250ms
execute the first setTimeout callback, which is nop
execute the second...
I can't think of a quick way to fix this, other than putting your processing code in the AJAX call back, which is where it should be for async code anyway.
Why not create an array of requests and just pop them off one-by-one when you get the response from the previous ajax call.
I am working on a network architecture which gives late response to a GET query. I wish to draw the image once I am able to receive from the server. I am trying to increase delay time of display function so that it can be drawn once fetched from server. I am using canvas to display picture from a particular URI. Here is the portion of code which I need to delay running :
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var img = new Image;
var strDataURI = nameStr;
img.onload = function(){
context.drawImage(img,0,0, 150,150); // Or at whatever offset you like
};
img.src = strDataURI;
Please help. Thanks in advance.
There are several options for this. If you insist on making this a timer then you can use setTimeout().
window.setTimeout(function() { // function code here }, 3000);
You could also set your ajax call to be synchronous instead of asynchronous. This will cause other functions to wait until it is complete before running.
$.ajax({
async: false
});
Finally you could put the draw function in the complete of your ajax call. This function is run after the ajax call is completed.
$.ajax({
complete: function(result) {
// code to perform the draw
}
});
Try setTimeout (reference here http://www.w3schools.com/js/js_timing.asp)
Suggestion
My suggestion is to NOT delay it at all. As chockleyc said, waiting for the response would be the best option.
Cases
There are the following possible scenarios:
You are making the GET request and waiting for the response
You are not making the request manually, and it is simply loaded with page
You make the GET request manually
If you are making the GET query yourself, my strong recommendation is to use Promise like this:
var getRequest = new Promise(function(resolve, reject){
//We call resolve(...) when what we were doing async succeeded, and reject(...) when it failed.
//In this example, we use setTimeout(...) to simulate async code.
var oReq = new XMLHttpRequest();
oReq.onload = function(e) {
resolve(oReq.response);
}
oReq.open("GET", "www.bananas.com");
oReq.send();
});
and then you would use it like:
var getRequest()
.then(console.log);
Which in this case would print the response. If you are not familiar I recommend the MDN Promises documentation.
Another alternative is to simply use the XMLHttp from JavaScript without promises. You can read more examples in the MDN documentation as well or if it is too confusing for you give a try to the kirupa tutorial.
You don't make the GET request manually
In this case, I recommend you listen to the GET request, and then perform a specific action once its response arrives. A good solution for this can be found in this response:
https://stackoverflow.com/a/3597640/1337392
Where you will find a mini library that will listen to ALL GET requests. This way, every time you receive a GET request you can filter it by what you want and execute your code.
If the previous code is too complex for you, you can also have a look at this much simpler alternative:
Listen to serve response JavaScript
You really insist in using a timer
The worst solution by far. Why? try answering these questions:
What happens if the image doesn't arrive before you expect? Instead of 1 second, what if it takes 2?
How do you know for sure exactly how long the GET request will take? Will you make a median of 100 requests? What if you get that one request that is outside the median?
Assuming you always wait the longest possible time (to ensure everything works) why should the majority of your users have a weaker experience?
But if you insist on it, then both answers from "chockleyc" and from "Johannes P" should clarify all your questions.
That's all for now, I hope it helps !
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.