Javascript asynchronous calls stepping on one another - javascript

I'm using AHAH (as outlined here http://microformats.org/wiki/rest/ahah) to make two calls to populate HTML on a page. The calls happen after the document is ready and are fired off one after another. The result, every time, is the first call gets overwritten with the last calls response. So I'll have two of the same chunks of HTML on the page instead of two unique pieces of code. Sometimes the first call doesn't even get to evaluate it's call back and thus remains empty.
Any ideas?

If you're using the exact code on that page, it's not surprising, as the example there uses a single global variable to store the XMLHttpRequest being made. So there's no way it can work for more than one simultaneous request: calling the function a second time overwrites the req with a new one, causing the req read by ahahDone to be the wrong request.
If you want to allow this you'll have to make req a local variable (by declaring it var in function ahah()), and pass it with the target to the ahahDone() function. Or just do it inline:
function Element_loadHTML(element, url) {
var req= null;
if (window.XMLHttpRequest) {
req= new XMLHttpRequest();
} else if (window.ActiveXObject) {
try {
req= new ActiveXObject('MSXML2.XMLHttpRequest');
} catch() {}
}
if (!req) {
element.innerHTML= 'Browser does not support XMLHttpRequest';
return;
}
element.innerHTML= 'Loading...';
req.onreadystatechange= function() {
if (req.readyState===4)
element.innerHTML= req.status===200? req.responseText : 'Error '+req.status;
};
req.open('GET', url);
req.send(null);
}
Element_loadHTML(document.getElementById('appdata'), 'appdata.part.html');
Element_loadHTML(document.getElementById('foo'), 'bar.part.html');
The stuff with the browser sniffing and trying to execute script tags is hopeless and broken; don't use it. It's not good practice to be loading <script> element content into the page.

Related

Why should we put onload before send method

I am new to JavaScript and would like to ask about about AJAX, that is, why we put xhr.onload before xhr.send() since even if I put xhr.onload after xhr.send() all works perfectly. But majority of tutorials teach you to put onload before send() without proper explanation. So, should I use
let btn = document.querySelector('button').addEventListener('click', function(){
let xhr = new XMLHttpRequest();
xhr.onload=function(){
if(this.status===200){
let div=document.querySelector('div').innerHTML=xhr.responseText;
}
}
xhr.open('GET', './mir.txt');
xhr.send();
})
Or
let btn = document.querySelector('button').addEventListener('click', function(){
let xhr = new XMLHttpRequest();
xhr.open('GET', './mir.txt');
xhr.send();
xhr.onload=function(){
if(this.status===200){
let div=document.querySelector('div').innerHTML=xhr.responseText;
}
}
})
and WHY?
Use your first version.
Logically, in the situation where the browser has the response cached, then XHR could complete instantly, and then you try to add "onload" after the response has already loaded, then nothing will happen.
In reality, even when cached, I don't think this can happen because of how the browser engine works, but from the coding point of view, it looks like it could happen. So making the pattern have onload at the top removes all suspicion that such behaviour could occur. Possibly in older browsers, when people did tend to do XHR reuests manually, that kind of thing was an actual danger?
I do know, in the scenario where you syncronously load a request, it does matter, because the thread will be blocked (as well as the whole window) during the send until it completes.
Onload is most frequently used within the element to perform a script once a website has fully loaded (including script files, images, CSS files, etc.) So Its a good approach lets load all the dependency after that we make a API call or Ajax call to update our DOM.
You don't have to use onload() before send(), your first and second example shows that already.
onload() is an event of XHR Object (a property for the load event of XHR), so it'll execute automatically when a particular event satisfies during the XHR execution. The function called when an XMLHttpRequest transaction completes successfully. So, Using onload() property you just define/tell what needs to do when load event satisfied. You don't have to define if you don't need it.
send() is a method of XHR, not an event. so you need to call it if you want it. see reference link to see more about its behavior for synchronous and asynchronous call.
Ref:
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/send
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequestEventTarget/onload

XMLHttprequest.send(null) is crashing my code

I'm currently writing a search function using JavaScript.
However, when I attempt to test my creation, I find that it stops about halfway through for no discernible reason.
Below is my code:
document.getElementById("test").innerHTML = "";
var Connect = new XMLHttpRequest();
Connect.open("GET", "xmlTest.xml", false);
document.getElementById("test").innerHTML = "1";
Connect.send(null);
document.getElementById("test").innerHTML = "2";
var docX = Connect.responseXML;
var linjer = docX.getElementsByTagName("linjer");
The first line is there to clear a potential error message from earlier in the code. Then I attempt to open up an XML file, as I need to read from it.
As you can see, I've entered two debug statements there; they will print 1 or 2 depending on how far I get in the code.
Using this, I've found that it stops exactly on the Connect.send(null); statement (as 1 gets printed, but 2 never does), but I can't figure out why. Google says that it might be that chrome can't access local files, but when I found a way to allow Chrome to do this, it still did not work.
What am I doing wrong?
This might be a synchronous issue that requires a response that your code simply is not getting.
Try using an async call instead:
Connect.open("GET", "xmlTest.xml", true);
Also make sure to setup proper callbacks since you'll be using async here now instead of synchronous code, like so:
// Global variable scope
var docX;
var linjer;
// Define your get function
getDoc = function(url, cbFunc) {
var Connect = new XMLHttpRequest();
// Perform actions after request is sent
// You'll insert your callback here
Connect.onreadystatechange = function() {
// 4 means request finished and response is ready
if ( Connect.readyState == 4 ) {
// Here is where you do the callback
cbFunc(Connect.responseXML);
}
};
// 'true' param means async, it is also the default
Connect.open('GET', url, true);
Connect.send();
}
// Define your callback function
callbackFunction = function(responseXML) {
// XML file can now be stored in the global variable
window.docX = responseXML;
window.linjer = window.docX.getElementsByTagName("linjer");
}
// And here is the call you make to do this
getDoc("xmlTest.xml", callbackFunction);
For better understanding of all of this, do some research on scope, closures, callbacks, and async.

Problems understanding/using callback function

In this discussion and in chat I understood that a callback is the only way to go!
"
Get from the server a link with ajax, write the link in a variable, open an xml with this link, doing some stuff with the xml: is callback the only way?
"
I'm trying to understand what a callback is. I read some blog, but I still have problems.
What I have now in JS is
1) a function to open an xml.
2) function to request the link of the xml in first function
Can someone provide an example in PLAIN JAVASCRIPT of how to nest these two functions?
The server generate the link of the xml because I'm making a multi user web site and every user has it's own xml. So I need to ask the server what is the link of the xml and then open it. Is there an easy way to achieve this? I need plain javascript no jquery.
Thanks!
In general, a "callback" is a function which will be executed at a later time when an asynchronous process is completed.
So you might start by defining the function that should happen when the data is retrieved from the server (the "second" function, intuitively, but you should define it first because it's the business functionality you're looking to achieve and not just an implementation concern). Something as simple as:
var doSomethingWithTheData = function () {
// do, well, something with the data
};
This assumes that you have the data, which you don't yet. But the AJAX call will get that data. You can now use this function as your callback for the AJAX call. Taking the AJAX example from MDN, you might have this:
var httpRequest;
if (window.XMLHttpRequest) { // Mozilla, Safari, ...
httpRequest = new XMLHttpRequest();
} else if (window.ActiveXObject) { // IE 8 and older
httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
}
httpRequest.onreadystatechange = doSomethingWithTheData;
// perform the AJAX request
The httpRequest object will contain the response from the server after the AJAX call is executed and completed. (Remember that this happens asynchronously, so it won't contain the response on the immediate next line of code. It will at some later time which you don't control. Hence the need for the callback.)
I recommend walking through that full MDN article to get all the details, particularly on handling errors and such. But the data you're looking for (assuming nothing went wrong) would be in httpRequest.ResponseText. So, also assuming your variables are scoped to allow this (you can modify that as needed):
var doSomethingWithTheData = function () {
var data = httpRequest.ResponseText;
// do, well, something with the data
};
Excuse me, #David
var httpRequest;
if (window.XMLHttpRequest) { // Mozilla, Safari,
httpRequest = new XMLHttpRequest();
} else if (window.ActiveXObject) { // IE 8 and older
httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
}
var url = "http://myserver.mydomain/getMyUsersXMLUrl?user=pete";
httpRequest.open("GET", url, true); // next ajax to retrieve XML - File
httpRequest.onreadystatechange = function() {
if (httpRequest.readyState == 4) { // response received
var response = httpRequest.responseText; // this should contain you url
httpRequest.open("GET", response, true); // next ajax to retrieve XML - File
// and the same as for the first request
}
}
If You just need to download a xml you dont need a callback . Just take a look at jquery and ajax. Retrieving a callback from a server is not possible . JSONP does deal with callbacks that are called by the server (actually they arent ) Butter you wont need it. I think you are messaging the xhttprequestobject
A callback function is simply a piece of executable code passed as a parameter to another piece of code. For example:
function first (arr, predicate) {
// no predicate supplied, return first element
if (!predicate) return arr[0];
for (var i = 0; i < arr.length; i++) {
// return first element satisfying predicate
if (predicate(arr[i])) return arr[i];
}
// no element satisfying predicate, return null
return null;
}
// second parameter is an anonymous function
// will alert 4, as it's the first element which will return true
alert(first([1, 2, 3, 4, 5, 6], function(n) { return n > 3; }));
Callbacks are useful for asynchronous tasks, or for library functions which need extra customization at runtime.

Synchronous AJAX call not truly synchronous

I'm having trouble with a synchronous AJAX request.
I am creating a web application where there are many AJAX requests called in sequence which should be returned in strict order. Rather than putting every succeeding request in the readystatechange event handler of the last request, I decided to simply call them synchronously.
However, in the following code, alert() is invoked before adding the response to the DOM.
window.addEventListener("load", main, false);
function main (e) {
// Sending synchronous request
var request = new XMLHttpRequest();
request.open("GET", fileName, false);
request.overrideMimeType("text/xml");
request.send(null);
// Receiving response
var response = request.responseXML;
// Changing XML Tree to corresponding XHTML Tree
response = XMLtoXHTML(response);
//Adding response to the body
document.body.appendChild(response);
// Calling alert
alert("Hello World");
}
The response gets in fact successfully added to the DOM, but only after clicking OK on the alert message. When I do a walkthrough using Safari's Script Debugging features, the response does get added to the DOM before calling alert().
Any suggestions?
NB: I left out other requests.
I guess it actually does get added, you just don't see the changes before JS returns control back to the browser, i.e. until alert window is closed. If you cannot do an async request (which I would suggest first), you could at least make dom changes in a separate setTimeouted "thread".
It has indeed been added to the DOM but the page hasn't parsed the new element into view yet. If I had to guess it would be that since alert blocks the current running JS thread webkit tries to perform reflows during this time for efficiency.
Test case:
function foo()
{
var div = document.createElement("div");
div.innerHTML = "hi friends";
div.id = "bar";
document.body.appendChild(div);
alert(document.getElementById("bar").innerHTML);
}
Do you need the element to appear before the alert comes up? Use this in that case
setTimeout(function(){alert("Hello World");}, 1);
Or use a flow control library like Frame.js (or asnyc, flow.js, more):
Frame(function(next)){
document.body.appendChild(response);
next();
});
Frame(function(next)){
alert("Hello World");
next();
});
Frame.init();

How to store variable within javascript to limit number of http calls?

I am using a second party file downloader which returns a progress event. I can capture the event and call a program on the server to perform an update (for security purposes so I can tell the most recent activity).
I get about 30 events per second all at percent downloaded 1%, then 30 more at 2%, then 30 more at 3%, etc. I would like to limit my http calls to only once per percentage change, 1%, 2%, 3%, etc. I would put a hidden field on the page and compare that and update it, but I cannot refresh the page since the download is in progress.
Is there a way to use some type of client side storage within javascript or jquery for this?
In other words, I need to be able to tell when the PercentCurrent value changes from 1 to 2, etc.
My javascript function looks like this:
function onProgress(PercentTotal, PercentCurrent, Index){
var xmlhttp;
//The handler will update the file progress
if (typeof XMLHttpRequest != 'undefined') {
xmlhttp = new XMLHttpRequest();
}
if (!xmlhttp) {
throw "Browser doesn't support XMLHttpRequest.";
}
var data = "";
xmlhttp.open("POST", "UpdateProgress.aspx?PercentCurrent=" + PercentCurrent, true);
//Send the proper header information along with the request
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
//xmlhttp.setRequestHeader("Content-length", data.length);
xmlhttp.setRequestHeader("Connection", "close");
xmlhttp.send(data);
}
Thank you,
Jim
JavaScript does indeed have variables, you just need to store one in a scope that's accessible to your onProgress code. You may just be able to use a var in the same place onProgress is declared, but a simple and JavaScripty way to make that variable "private" is to use a closure:
var onProgress = (function(){
var lastSend = 0;
return function(PercentTotal, PercentCurrent, Index){
if (Math.floor(PercentCurrent) > lastSend) {
lastSend = PercentCurrent;
var xmlhttp…
}
}
})();
This'll look a little confusing if you haven't worked with JavaScript much. Here's what's going on:
I create a variable called onProgress
I create and immediately run an anonymous (unnamed) function, like this: (function(){ … })()
This function defines a local variable, lastSend, and returns the real onProgress function.
Whenever a function is called in JavaScript, it has access to the scope in which it was created. So, whenever onProgress() is called, it'll have access to the lastSend variable, and can check that progress is has moved past the next whole percent.
Of course, this is a bit ugly, and it can only be used once on a page (since there's only one closure with one lastSend variable. Instead of assigning it to a name, you might pass it directly into the function which calls it, anonymously (see below). Then, a new copy of the function, with a new closure, gets created when you hit downloadFile.
Your original question is tagged jquery. If you are indeed using jQuery on the page, you can simplify the posting of data significantly (down to one line) and make it more compatible, with jQuery.post:
$.post("UpdateProgress.aspx", { PercentCurrent: PercentCurrent });
(This would replace all the XMLHTTPRequest-related code in onProgress.)
So, using a closure and jQuery.post might look like this:
// Not sure what your second-party file downloader looks like
fileDownloader.downloadFile((function(){
var lastSend = 0;
return function(PercentTotal, PercentCurrent, Index){
if (Math.floor(PercentCurrent) > lastSend) {
lastSend = PercentCurrent;
$.post("UpdateProgress.aspx", { PercentCurrent: PercentCurrent });
}
}
})());
Have a look at jQuery's .data(). It allows you to store data and attach it to a particular DOM element like so:
$('body').data('foo', 52);
$('body').data('foo'); // 52
I am not sure to understand your problem. Is the page continously reloaded? If it is not all that you need to do is:
var lastPercent = null; // you need to initialize this when it all starts again.
function onProgress(PercentTotal, PercentCurrent, Index){
var xmlhttp;
if (lastPercent == PercentCurrent)
return; //Does nothing if no change occurred.
lastPercent = PercentCurrent;
//The handler will update the file progress
if (typeof XMLHttpRequest != 'undefined') {
xmlhttp = new XMLHttpRequest();
}
if (!xmlhttp) {
throw "Browser doesn't support XMLHttpRequest.";
}
var data = "";
xmlhttp.open("POST", "UpdateProgress.aspx?PercentCurrent=" + PercentCurrent, true);
//Send the proper header information along with the request
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
//xmlhttp.setRequestHeader("Content-length", data.length);
xmlhttp.setRequestHeader("Connection", "close");
xmlhttp.send(data);
}

Categories

Resources