Loading configuration async in JavaScript, how best to wait for response? - javascript

This is what I am trying to do:
Request a JSON object from remote server script
WAIT until the JavaScripts gets all the response data
print a value from the response object
I'm trying to use setTimeout in the get config function to recall itself after 1 second if the value is undefined, when it's no longer undefined do something with the value.
Alternatively it seems I could just create some method that loops for some number of seconds, but i'd like to avoid that if there is a better way to accomplish what i'm trying to do? My current code just seems to recurse without any delay which breaks my runtime
Thanks for any ideas, heres the code:
function runApplication() {
var initialiser = new Initialiser();
var config = new Config();
initialiser.fetchConfigurations(config);
config.alertValue('setting1');
}
function Initialiser() {
debug.log("Started");
}
Initialiser.prototype.fetchConfigurations = function(config) {
var req = new XMLHttpRequest();
var url = CONFIGURATION_SERVER_URL;
req.onreadystatechange = function() {
if (req.readyState == 4 && req.status == 200) {
var configObject = eval('(' + req.responseText + ')');
config.setConfig(configObject);
} else {
debug.log("Downloading config data...please wait...");
}
}
req.open("GET", url, true);
req.send(null);
}
function Config() {
this.config
}
Config.prototype.setConfig = function(configObject) {
this.config = configObject;
}
Config.prototype.getValue = function(setting) {
if(this.config === undefined) {
setTimeout(this.getValue(setting), 1000);
} else {
return this.config[setting];
}
}
Config.prototype.alertValue = function(setting) {
if(this.config === undefined) {
setTimeout(this.alertValue(setting), 1000);
} else {
alert(this.config[setting]);
}
}

From what I'm looking at, you should just add an extra step to .setConfig, which handles the next piece.
Config.prototype.setConfig = function (data) {
this.config = data;
this.doSomethingAfterTheDataExists();
};
There are lots of ways to do this...
...writing a moderator/observer/pub-sub implementation is dirt-simple, and could be done in relatively few lines for the benefit given...
Writing a Promise system would be even more powerful, but would require more work (because it's like the above implementations, with an added layer of abstraction, to make a really clean and straightforward interface for async).
The other alternative to having .setConfig publish a message, or fire another method, would be to call another method, after .setConfig in the AJAX call.
xhr.onreadystatechange = function () {
// .......
config.setConfig(/* ... */);
config.doSomethingAfterTheDataExists();
};
If you're making multiple AJAX calls, with multiple assignments to multiple properties, and you want to wait until every single call is done, then you really should look into implementing a Promise system.
It will save your sanity and would be well-worth the ~60-100 lines which you could minify into oblivion.
If you're just waiting for that one response to come back, then keep in mind, what happens after the onreadystatechange says that it's finished and the data's there (your success check) is synchronous.
The waiting is the async part.
Once it's delivered, everything (including the stuff inside of that check), goes back to being procedural.

Just use jQuery and getJSON with success/done callback.

You could use a synchronous call instead of asynchronous call for XMLHttpRequest. But this may not be the best option if your your users need to interact with the web page while you wait for a response.
...
req.open("GET", url, false);
request.send(null);

Related

How to pass ajax call result to subsequent ajax calls

Im currently trying to utilize multiple ajax calls through ajax chaining, but unsure on the best approach since there are a few ways to do this via new frameworks, jquery and pure javascript.
I would prefer to do this with pure vanilla javascript given the native development on js has improved a lot over recent years, however, in the instance of multiple ajax calls, i believe there is still plenty to be improved upon, i believe one of the ways would be to use promises ? i do see many deal with this occurrence via jquery.
i would be very appreciative if fellow coders could give their example as to how they would code a modern approach ajax chain call dependent on ajax returned call values preceding it.
ok, so in short, i am attempting to pass the value of the first ajax call to the second ajax call, along with identifying the correct way of executing the second ajax call.
Below i have added code with comments:
// Establish functionality on window load:
window.onload = function() {
'use strict';
// get product id on load
var pid = document.getElementById('pid');
var colorlist = document.getElementById('colorlist');
var sizelist = document.getElementById('sizelist');
colorlist.onclick = function(e) {
if (typeof e == 'undefined') e = window.event;
var colorid = e.target.value
while (sizelist.firstChild) {
sizelist.removeChild(sizelist.firstChild);
}
// 2ND AJAX CALL
var xhr = getXMLHttpRequestObject();
xhr.open('GET', '/ajax/get_sizes.php?id=' + encodeURIComponent(pid.value) + '&colorid=' + encodeURIComponent(colorid), true);
// set header if sending to php
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(null);
// Function to be called when the readyState changes:
xhr.onreadystatechange = function() {
// Check the readyState property:
if (xhr.readyState == 4) {
// Check the status code:
if ( (xhr.status >= 200 && xhr.status < 300)
|| (xhr.status == 304) ) {
var sizes = xhr.responseText;
var sizes = JSON.parse(sizes);
for (var i = 0, num = sizes.length; i < num; i++) {
var label = document.createElement('label');
label.setAttribute ("for", sizes[i].id);
label.classList.add("swatch");
label.innerHTML = sizes[i].size;
var radio = document.createElement('input');
radio.type = "radio";
radio.id = sizes[i].id;
radio.value = sizes[i].id;
radio.name = "sizes";
sizelist.appendChild(label);
sizelist.appendChild(radio);
} //END OF FOR LOOP
} else { // Status error!
document.getElementById('output').innerHTML = xhr.statusText;
}
} // End of readyState IF.
}; // End of onreadystatechange anonymous function.
}; // END OF COLORLIST ONCLICK
// 1ST AJAX CALL
var ajax = getXMLHttpRequestObject();
// Function to be called when the readyState changes:
ajax.onreadystatechange = function() {
// Check the readyState property:
if (ajax.readyState == 4) {
// Check the status code:
if ( (ajax.status >= 200 && ajax.status < 300)
|| (ajax.status == 304) ) {
var colors = ajax.responseText;
var colors = JSON.parse(colors);
for (var i = 0, num = colors.length; i < num; i++) {
var label = document.createElement('label');
label.setAttribute ("for", colors[i].id);
label.classList.add("swatch", colors[i].color);
label.innerHTML = colors[i].color;
var radio = document.createElement('input');
radio.type = "radio";
radio.id = colors[i].id;
radio.value = colors[i].id;
radio.name = "colors";
colorlist.appendChild(label);
colorlist.appendChild(radio);
} // END OF FOR LOOP
} //END OF STATUS CODE CHECK
else { // Status error!
document.getElementById('output').innerHTML = ajax.statusText;
}
} // End of onreadyState IF.
}; // End of onreadystatechange anonymous function.
ajax.open('GET', '/ajax/get_colors.php?id=' + encodeURIComponent(pid.value), true);
// set header if sending to php
ajax.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
ajax.send(null);
}; // End of onload anonymous function.
Regards David
Welcome to SO and thank you for your question. I'll do my best to show you some examples of how you could execute your code in a way that might be preferable to you as a solution.
Callbacks
What is a callback?
Simply put: A callback is a function that is to be executed after another function has finished executing — hence the name ‘call back’.
Source of quote
In your code example you want to execute at least 2 HTTP request after one another. This would mean that a piece of code has to be executed twice. For this you can write a function around the XMLHTTPRequest piece to be able to execute it multiple times when writing it down only once.
The function below here has two parameters: url and callback. The url parameter is a string which will be injected in the second parameter of the xhr.open method. The callback parameter will be a function. This function will be called when the request has been succesfully finished.
function get(url, callback) {
var xhr = new XMLHTTPRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
if ('function' === typeof callback) {
callback(xhr.responseText);
}
}
};
xhr.open('GET', url, true)
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send();
}
Here is a little example of how it would work. See that the callback function has a parameter called data1. That is the xhr.responseText that we got back from the XMLHTTPRequest. Inside the callback function call the get function again to make another request.
get('/ajax1.php', function(data1) {
// Do something with data1.
get('/ajax2.php', function(data2) {
// Do something with data2.
});
});
This is a fairly simple way to make a request after another is finished.
But what if we have 100 requests after each other?
Promisified XMLHTTPRequest
The Promise object represents the eventual completion (or failure) of an asynchronous operation, and its resulting value.
Source of quote
Enter Promises. The example below here is almost the same as the example above. Only this time we use a Promise. When calling get we immediately return a Promise. This promise will wait for itself to either resolve or reject. In this case we only use resolve for successful requests. Whenever the request has finished resolve is called and the Promise chain begins.
function get(url) {
return new Promise(resolve => {
var xhr = new XMLHTTPRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
if ('function' === typeof callback) {
resolve(xhr.responseText);
}
}
};
xhr.open('GET', url, true)
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send();
});
}
So instead of using a callback function we use then. Inside then we do use a callback function which allows us to use the value that has been resolved in the returned Promise. then can be chained with more then's inifinitely until you run out of things to chain.
Inside the callback function call the next request.
get('/ajax1.php')
.then(data1 => {
// Do something with data1.
get('/ajax2.php')
.then(data2 => {
// Do something with data2.
});
});
Fetch
The Fetch API provides an interface for fetching resources (including across the network). It will seem familiar to anyone who has used XMLHttpRequest, but the new API provides a more powerful and flexible feature set.
Source of quote
Before we've created our own Promise version of an XMLHTTPRequest. But JavaScript has evolved an has gotten new tools to work with. fetch kind of looks like how our get function works, but has way more features and options to make it more powerful. And it also uses promises!
fetch('/ajax1.php')
.then(response1 => response1.text())
.then(data1 => {
// Do something with data1.
fetch('/ajax2.php')
.then(response2 => response2.text())
.then(data2 => {
// Do something with data2.
});
})
.catch(error => console.log(error));
Though the then syntax still makes us nest functions. Like before, what about when you have 100 callback functions to call? That will be a nesting mess!
Fetch + Async/Await
Now this is the way to tackle the nesting problem and make the syntax more like assigning a simple variable in JS. Async/Await is a part of modern JavaScript, so be wary of older browsers not supporting it. Check caniuse for the current support.
The Async/Await syntax works like the following. Create a function with the async keyword in front of it. This will indicate, like it implies, that async code will be performed here. It also makes the function with async before it automatically return a Promise.
Inside the async function use the await keyword whenever you call a function which returns a Promise, like fetch, or our own function get. This will return the resolved value without having to use then or a callback function.
The await keyword also makes the code actually wait before continueing to the next line of code. Now your JS looks nice and can be written with less nesting.
(async function() {
const response1 = await fetch('/ajax1.php');
const data1 = await response1.text();
// Do something with data1.
const response2 = await fetch('/ajax2.php');
const data2 = await response2.text();
// Do something with data1.
}());
I really hope this is helpful and helps you get where you need to be going.
If you have any questions regarding the above, please let me know!
Have a good one!
You can use Promise chain which could be like the example mentioned below:
.then(function(result) {
return doSomethingElse(result);
})
.then(function(newResult) {
return doThirdThing(newResult);
})
.then(function(finalResult) {
console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);enter code here
You can the documentation mentioned below:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises

Return result of XMLHttpRequest

I'm trying to return the result of an XMLHTTPRequest:
Click me for Google CDN jQuery!
<script>
const url = {
httpRequest: function(callback) {
var xhr = new XMLHttpRequest();
xhr.addEventListener("load", callback);
xhr.open("GET", document.querySelector("a").href); // Using querySelector to simulate how my real program works - I don't want to hardcode anything mainly because this should be dynamic.
xhr.send("");
},
compileData: function(data) {
var response = data.target.responseText.substring(4, 17)
// I can't figure out how to take this response and 'return' it.
},
getContent: function() {
url.httpRequest(url.compileData)
}
}
var content = url.getContent() // I want 'content' to be equal to "jQuery v3.3.1"
</script>
But, I can't figure out how to 'return' the response.
Yes, I know that there are other questions out there like this one, namely: How do I return the response from an asynchronous call? But, I'm new to JavaScript and have no idea how to integrate what they're saying there into my case.
A few options for applying the guidance given in "How do I return the response from an asynchronous call?"
If you'd like to continue using callbacks, then it's a matter of adding another. The result you'll want is to allow your entry point to accept its own:
url.getContent(function (content) {
// proceed with `content` here
});
So, getContent needs to expect the argument:
getContent: function(callback) {
// ...
}
But, it also needs to call the callback, which it can do with yet another intermediate function, so it can combine callback with compileData:
getContent: function(callback) {
url.httpRequest(function (data) {
callback(url.compileData(data));
});
}
You can also use Promises throughout the url object.
Starting with httpRequest, you can utilize jQuery.ajax() and its shortcuts returning a customized promise object – a jqXHR:
httpRequest: function () {
return Promise.resolve(
jQuery.get(document.querySelector("a").href)
);
}
In this, Promise.resolve() can be used to convert the customized promise into a standard Promise.
Then, getContent can be modified to interact with the promise, modifying the response with compileData:
getContent: function () {
return url.httpRequest().then(url.compileData);
}
And, the caller can add its own handler using the Promise's methods:
url.getContent().then(function (content) {
// proceed with `content` here
});
Also, a brief aside... In supporting browsers, you can use fetch() in place of jQuery.ajax(), which already returns a standard Promise, but will first give you a Response object rather than the raw data.
httpRequest: function () {
return fetch(document.querySelector("a").href)
.then(response => response.text())
}
There are three different ways according to 3 JS standards i.e es5,es6 and es7 but here you have used xhr therefore it is an old standard.
Hence, you should use xhr.onload method of xhr object over here to return the response. Simply do:
xhr.onload = callback;
insert this code between xhr.open and xhr.send.
You need Promises, Promises and more Promises! And you should return one from your function.
function httpRequest() {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.onload = (response) => {
resolve(response); // You should check the response and maybe preprocess it
// so that not everything is passed further
}
xhr.open("GET", document.querySelector("a").href);
xhr.send("");
});
}
and then you may use then and catch
httpRequest().then((data) => {
// Yay! Got response from the server!
}).catch((error) => {
// Yay! More errors!
});

Javascript rendering. How to write Javascript so it doesn't continue executing code before a function call has ended

Not sure if my question is subjective/objective but as a JavaScript newbie i'm encountering this problem quite a lot. So here I go.
I'm used to write C#, so my JavaScript structure looks like C#. And just that, that gives problems I think ;-)
Let's give a simple example where I met my problem again today:
MyLibrary.fn.InitAddEntityForm = function () {
$('a#btnAddEntity').click(function () {
//post data and receive object with guid and isPersisted boolean
var persistedObject = MyLibrary.fn.CheckAndSendAddEntityForm("name", "avatarurl.png");
console.log("test");
//check if persisted and go to next step
if (persistedObject.isPersisted) {
MyLibrary.fn.InitAddAnotherEntityForm(persistedObject.gdEntityId);
} else {
alert("Oops, something went wrong. Please call 911");
}
});
};
//////*****/////
//SOME FUNCTION THAT SENDS MY FORM AND RETURNS AN OBJECT WITH TRUE VALUE AND POSTED ENTITY ID
/////*****//////
MyLibrary.fn.CheckAndSendAddForm = function (txtName, ImageUrl) {
var postUrl = "/admin/add";
var persistedObject = new Object();
$.post(
postUrl,
{ Name: txtName, ImageUrl: txtImageUrl},
function (data) {
if (data.Status == 200) {
console.log("Post status:" + data.Message);
persistedObject.isPersisted = true;
persistedObject.gdEntityId = data.Data;
} else if (data.Status == 500) {
console.log("Failed to post entitiy");
} else {
console.log("Fault with Javascript");
}
}, "json"
);
return persistedObject;
};
Okay, thats it. Everything looks okay right? Browser says no.
I tried to debug it using firebug, looping over my code line by line, and that way the browser does what I want: Execute a new function to show the next panel in my wizard.
After placing a lot of Console.logs() in my code I figured out that this must be something about timing in JavaScript. In C# the code executes line by line, but apparently JavaScript doesn't.
By placing that Console.log("test") I noticed that "test" appeared in my console before "Post status: Success!".
So here's my question, how should I write my JavaScript code so I have control over the way the browser executes my code?
Should I really replace the code below to the end of my CheckAndSendAddEntityForm()?
//check if persisted and go to next step
if (persistedObject.isPersisted) {
MyLibrary.fn.InitAddAnotherEntityForm(persistedObject.gdEntityId);
} else {
alert("fout");
}
Is this how I have to write JavaScript: One big domino effect or am I just doing something wrong?
$.post is a shortcut for an AJAX call, AJAX is by definition asynchronous, which means it won't wait on a response before continuing processing. If you switch it to a regular AJAX() method, there is an async option you can set to false, which will make it behave as you are expecting.
Alternatively you can also define a function to execute on successful return of the AJAX request, in which you can call the next step in your process chain.
The AJAX call is asychronous; that means that the callback method exposes by $.post will be executed when the request completes, but your javascript will continue executing as soon as the invoke to $.post finishes. If you want to do something after the ajax call is done, you need to provide a callback method and do something else, ex:
MyLibrary.fn.CheckAndSendAddForm = function (txtName, ImageUrl, callback) {
var postUrl = "/admin/add";
var persistedObject = new Object();
$.post(
postUrl,
{ Name: txtName, ImageUrl: txtImageUrl},
function (data) {
if (data.Status == 200) {
console.log("Post status:" + data.Message);
persistedObject.isPersisted = true;
persistedObject.gdEntityId = data.Data;
} else if (data.Status == 500) {
console.log("Failed to post entitiy");
} else {
console.log("Fault with Javascript");
}
callback(); // This is where you return flow to your caller
}, "json"
);
};
Then you invoke like so:
var persistedObject = MyLibrary.fn.CheckAndSendAddEntityForm("name", "avatarurl.png", function()
{
console.log("test");
//check if persisted and go to next step
if (persistedObject.isPersisted) {
MyLibrary.fn.InitAddAnotherEntityForm(persistedObject .gdPronoId);
} else {
alert("Oops, something went wrong. Please call 911");
}
});
JavaScript is single-threaded. If you have asynchronous functionality, a simple boolean semaphore variable will help not to allow invocations of a function while some processes are running.
If you want to execute asynchronous tasks one by one (like a domino line), you will need to use callback functions.
What you're encountering is the "asynchronous" bit of AJAX. If you want to physically (as in the line line by line in the Javascript file) you can use the .success,.pipe or .done jQuery methods to add a callback to process the data further. Don't embed your callbacks if you can help it, or you will get a "domino effect" as you call it.

Queue multiple AJAX requests while offline, send when connection returns

I have an AJAX intensive application that requires sending multiple AJAX requests rapidly or concurrently. The following code is just a simple wrapper for sending AJAX POST calls I use throughout the app. There are 2 caveats:
1) I want to be able to test the user's internet connection before making the request, so I can notify them if their connection is down.
2) If their connection is down and they continue to use the app, which generates more AJAX calls, I want to queue those calls and send them one by one once connectivity returns.
The connectivity check and queueing work, however when the user comes back online only some of their requests are sent to the server, and they seem to be sent out of their original order. What am I missing? Why aren't all the requests being sent, and why aren't they in order?
And before anyone notes, I have seen a few other solutions on this topic involving jQuery. I'm not opposed to using those, I just want to understand why THIS code isn't working. Thanks in advance.
window.connectionState = true
window.xhrQueue = []
window.pingInterval
function xhrPost(url, packet, before, after) {
if (!url || typeof(url) !== "string") {
console.log("invalid url supplied in xhr call.")
return false
}
var mainRequest = function() {
var xhr= new XMLHttpRequest()
if (typeof(after) === "function") {
xhr.onreadystatechange = function(){
if (xhr.readyState == 4) {
after(xhr)
return true
}
}
}
if (typeof(before) === "function") {
before()
}
xhr.open("POST",url,true)
if (packet) {
xhr.send(JSON.stringify(packet))
}
else {
xhr.send()
}
}
ping(mainRequest)
}
function ping(mainRequest) {
// Create pingXhr to test connection
var pingXhr = new XMLHttpRequest()
pingXhr.onreadystatechange = function(){
// If pingXhr comes back successfully...
if (pingXhr.readyState == 4) {
if (pingXhr.status == 200) {
// If pingXhr comes back from being down, update user
if (window.connectionState !== true) {
setTimeout(function() { alert("And we're back! Your connection seems to be working now. Keep editing.") }, 1)
}
// If there are requests waiting, send them in order, then remove them
if (window.xhrQueue.length > 0) {
for (var i in window.xhrQueue) {
ping(window.xhrQueue[i])
window.xhrQueue.splice(i, 1)
clearInterval(window.pingInterval)
}
}
// Otherwise, just make the singular request
else {
mainRequest()
}
// Reset xhrQueue since stuff is successful, change connection to true, and unset onbeforeunload message
window.xhrQueue = []
window.connectionState = true
}
// If there was a problem with the request
else {
// Notify the user their internet is down
if (window.connectionState === true) {
setTimeout(function() { alert("It seems you have momentarily lost internet connectivity.") }, 1)
}
// If there are no requests in the xhrQueue, create the timeout. Otherwise, just add to the queue
if (window.xhrQueue.length === 0) {
window.pingInterval = setInterval(function(){ ping() }, 3000)
}
// Add the request to the xhrQueue to be processed in order
if (typeof(mainRequest) === "function") {
window.xhrQueue.push(mainRequest)
}
window.connectionState = false
}
}
}
pingXhr.open("GET","/some/url/here",true)
pingXhr.send()
}
It looks like you're using push() to place entries on the queue, then using splice() in a loop to remove them. That's not likely to work properly - it'll skip some/most of them, because the splice modifies the indexes in the array as you're iterating over them.
If you change your loop to always take the first element off, it'll work better.
edited to add: You probably don't want to do a for-in loop here, either. Modifying the keys of the object while you're iterating over it is not generally a good idea.
something like:
while (window.xhrQueue.length > 0) {
ping(window.xhrQueue[0]);
window.xhrQueue.splice(0, 1);
}
Or instead of trying to run all of the queued requests simultaneously, you could have the onreadystatechange handler grab the next entry off the queue and just send that request.
it will be because you are firing them all off at once, some will take longer to get back than others so their handlers will run first.
I suggest you send them one at a time using the callback to send the next one

Loading remote data, caching, and continuing in javascript

Basic use case. I have a global variable where I store remotely pulled data. If there is no data then I want to load it initially, wait for it load, and then continue processing. I don't really want to use a synchronous process if I don't have to.
Consider something like this where _companies is a global variable...
if (_companies === undefined || _companies.length == 0) {
loadExternalData();
}
// do something with the data in _companies
I feel like I'm missing something obvious. I understand that I can call async = false but that seems like a cludge. I could also put all the code in the block in a function, make an if..else and then call the function from loadExternalData() as well as in my else statement but again that seems like a cludge. It seems like I should be able to wrap that entire thing in a callback but I don't know how to do that.
Have a look at the code below, including the comments. The code has the same structure as the functions in your question. If anything is unclear, add a comment.
var companies; //Cache
function loadExternalData(callback){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){ //Set readystatechange event
if(xhr.readyState == 4 && xhr.status == 200){ //Request finished: 200 OK
callback(xhr.responseText); //Call callback func with data
}
}
xhr.open("get", "http://example.com", true); //True = async
xhr.send(null);
}
function parseData(data){
if(data) {//If data is defined, set companies
_companies = data;
}
if (typeof _companies == "undefined" || _companies.length == 0) {
loadExternalData(parseData); //Pass callback function
return; //No data, return function
}
//When the function reaches this point, _companies exist.
//Normal function behavior
}
See also: MDN: Using XMLHttpRequest

Categories

Resources