When I pass LoadTask2 as a callback, I am getting LoadTask2 executed before reading the text file done. what could be the reason and how can I make LoadTask2 to be executed after reading the text file?
LoadTask1(LoadTask2);
function LoadTask1(LoadTask2) {
let parEtask1Pending = document.getElementById('task1Status');
parEtask1.replaceChild(loadingE, parEtask1Pending);
currentIntervalId = setInterval(function() {
readTextFile("file1.txt", getTextData);
}, 2000);
LoadTask2();
}
function LoadTask2() {
console.log("Task2")
}
function readTextFile(file, callback) {
console.log('Reading file..')
var rawFile = new XMLHttpRequest();
rawFile.open("GET", "textFiles/" + file, true);
rawFile.onload = function() {
if (this.status === 200) {
getTextData(this.responseText);
}
}
rawFile.send();
}
Output comes as: Task2->Reading file.
The problem is here:
function LoadTask1(LoadTask2) {
let parEtask1Pending = document.getElementById('task1Status');
parEtask1.replaceChild(loadingE, parEtask1Pending);
currentIntervalId = setInterval(function() {
readTextFile("file1.txt", getTextData);
}, 2000);
LoadTask2();
}
javascript wont wait for the setInterval till its completed. The setInterval gets executed and starts to run "parallel".
Possible solution is to add an promise in your readTextFile like this:
readTextFile = function(file) {
return new Promise(function(resolve, reject){
console.log('Reading file..')
var rawFile = new XMLHttpRequest();
rawFile.open("GET", "textFiles/" + file, true);
rawFile.onload = function() {
if (rawFile.status === 200) {
resolve(rawFile.responseText);
}
rawFile.send();
}
}
now you can put an .then() on your function:
function LoadTask1(LoadTask2) {
let parEtask1Pending = document.getElementById('task1Status');
parEtask1.replaceChild(loadingE, parEtask1Pending);
readTextFile("file1.txt").then(function(response){
console.log("your response: " + response);
LoadTask2();
})
}
There is also another modern solution with async/await. You can do it like this too:
async function LoadTask1(LoadTask2) {
let parEtask1Pending = document.getElementById('task1Status');
parEtask1.replaceChild(loadingE, parEtask1Pending);
const responseText = await readTextFile("file1.txt");
console.log(responseText);
LoadTask2();
}
This should work, i didnt tested it yet.
One more thing here in this function:
function readTextFile(file, callback) {
console.log('Reading file..')
var rawFile = new XMLHttpRequest();
rawFile.open("GET", "textFiles/" + file, true);
rawFile.onload = function() {
if (this.status === 200) {
getTextData(this.responseText);
}
}
rawFile.send();
}
You actually dont pass nothing back to your callback function. you should rename getTextData(this.responseText) to callback(this.responseText) and then execute your task2 in your callback function like this:
function readTextFile(file, callback) {
console.log('Reading file..')
var rawFile = new XMLHttpRequest();
rawFile.open("GET", "textFiles/" + file, true);
rawFile.onload = function() {
if (this.status === 200) {
callback(this.responseText);
}
}
rawFile.send();
}
function LoadTask1(LoadTask2) {
let parEtask1Pending = document.getElementById('task1Status');
parEtask1.replaceChild(loadingE, parEtask1Pending);
readTextFile("file1.txt", function(response){
console.log(response);
LoadTask2();
});
}
That's because it's due to the setInterval function, it will delay for 2 seconds before it execute your function.
Related
I was trying to display an ajax request from a web service in my HTML.
I can display the result in the console but can not retrieve the result and then display it in my HTML.
I want to display the result of the request in "weather-result" div by clicking the "ask-weather" button.
Her is my code.
Thank everyone.
const askWeather = function(result){
var request = new XMLHttpRequest();
request.onreadystatechange = function subFunction() {
if (this.readyState == XMLHttpRequest.DONE && this.status == 200) {
result = JSON.parse(this.responseText);
return result.current_condition.condition;
}
};
request.open("GET", "https://www.prevision-meteo.ch/services/json/paris");
request.send();
}
const ask = document.getElementById('ask-weather');
ask.addEventListener('click', function(){
const weatherResult = document.getElementById('weather-result');
weatherResult.innerHTML = askWeather();
});
<html>
<head>
<link rel="stylesheet" type="text/css" href="base.css">
</head>
<body>
<div><button id="ask-weather">Quelle est la météo sur Paris ?</button></div>
<div id="weather-result"></div>
<script type="text/javascript" src="index.js"></script>
</body>
</html>
This seems like a asynchronous/callback problem here. When the button is clicked, it sends a request somewhere and will return right away (with undefined in case of the code above - this can be checked by saving it in a variable and console.log it).
When askWeather() is called, it could return something itself. The return in request.onreadystatechange cannot return for askWeather as it's happening multiple times and later - after askWeather is done and the request is being sent.
If you pass a function a variable and set it to something new in its body, it will not be changed for the caller. That means doing result = ... does not really help, if you wanted to pass a variabel and get it set by the inner function.
A different approach is necessary to handle this. Here are a few alternatives:
To keep it as most similar to the code you had, you can set the innerHTML in the onreadystatechange function:
const askWeather = function() {
var request = new XMLHttpRequest();
request.onreadystatechange = function subFunction() {
if (this.readyState == XMLHttpRequest.DONE && this.status == 200) {
const result = JSON.parse(this.responseText);
// set it here directly
const weatherResult = document.getElementById('weather-result');
weatherResult.innerHTML = result.current_condition.condition;
}
};
request.open("GET", "https://www.prevision-meteo.ch/services/json/paris");
request.send();
}
const ask = document.getElementById('ask-weather');
ask.addEventListener('click', function() {
askWeather();
});
Make it more general and let askWeather use a callback (call a function when it's "done"):
const askWeather = function(callback) {
var request = new XMLHttpRequest();
request.onreadystatechange = function subFunction() {
if (this.readyState == XMLHttpRequest.DONE && this.status == 200) {
const result = JSON.parse(this.responseText);
// send the result to the passed "callback" function
callback(result.current_condition.condition);
}
};
request.open("GET", "https://www.prevision-meteo.ch/services/json/paris");
request.send();
}
const ask = document.getElementById('ask-weather');
ask.addEventListener('click', function() {
askWeather(function (result) { // this whole function is the "callback" parameter
const weatherResult = document.getElementById('weather-result');
weatherResult.innerHTML = result;
});
});
(a) Let askWeather return a promise and use it in the caller
const askWeather = () => new Promise((resolve, reject) => {
var request = new XMLHttpRequest();
request.onreadystatechange = function subFunction() {
if (this.readyState == XMLHttpRequest.DONE && this.status == 200) {
const result = JSON.parse(this.responseText);
// send the result to the passed "callback" function
resolve(result.current_condition.condition);
}
// not sure about the error path here, but something like this:
if (this.readyState == XMLHttpRequest.DONE && this.status != 200) {
reject(new Error("There was an error with the XMLHttpRequest!"));
}
};
request.open("GET", "https://www.prevision-meteo.ch/services/json/paris");
request.send();
});
const ask = document.getElementById('ask-weather');
ask.addEventListener('click', function() {
askWeather()
.catch((err) => weatherResult.innerHTML = err.message) // to handle possible errors, maybe?
.then((result) => { // like the callback solution, but as promise!
const weatherResult = document.getElementById('weather-result');
weatherResult.innerHTML = result;
});
});
});
(b) Additionally to the Promise solution, in newer browsers there is already async and await syntax:
ask.addEventListener('click', async function() {
try {
const result = await askWeather(); // this "pauses" until the Promise return of `askWeather` resolves (or throws an error if it doesn't)
const weatherResult = document.getElementById('weather-result');
weatherResult.innerHTML = result;
} catch (e) {
// error could be handled here
}
});
Instead of XMLHttpRequest, use the fetch API, which usually should be available if Promises are available in the browsers you support. The solution is in the comments of the original question. With most modern browsers, this should work:
ask.addEventListener('click', async () => {
const response = await fetch("https://www.prevision-meteo.ch/services/json/paris");
const result = await response.json();
const weatherResult = document.getElementById('ask-weather');
weatherResult.innerHTML = result.current_condition.condition;
});
If you don't have to support IE, I would use the fetch alternative.
I hope the other alternatives make it clear, asynchronous patterns can be resolved in JavaScript.
function getJSON(path) {
return new Promise(function(resolve, reject) {
var xhttp = new XMLHttpRequest();
xhttp.open('GET', path, true);
xhttp.onreadystatechange = function() {
if (this.readyState === 4) {
if ((this.status >= 200 && this.status < 300) || this.status === 304) {
var response = JSON.parse(this.responseText);
resolve(response);
} else {
var error = this.statusText;
reject('Http/App Error: ' + error);
}
}
}
xhttp.onerror = processError;
xhttp.onabort = processError;
xhttp.send();
xhttp = null;
function processError(err) {
reject('Network Error: ' + err.target.status);
}
});
}
const ask = document.getElementById('ask-weather')
const weather = document.getElementById('weather-result')
const endpoint = 'https://www.prevision-meteo.ch/services/json/paris'
ask.addEventListener('click', function() {
getJSON(endpoint).then((success) => {
const response = success.current_condition.condition
weather.innerHTML = response
}, (error) => {
console.log(error)
})
})
This is a simple example using Promise. See the fiddle working.
var address = [ "data/somedata1.json", "data/somedata2.json", "data/somedata3.json", "data/somedata4.json", "data/somedata5.json"];
and function to import this file
function readData()
{
var loadFile = function (filePath, done)
{
var xhr = new XMLHttpRequest();
xhr.open("GET", filePath, true);
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
xhr.onload = function () { return done(this.responseText) }
xhr.send();
}
address.forEach(function (file, i)
{
loadFile(file, function (responseText)
{
jsonData[i] = JSON.parse(responseText);
if(i === 4)
{
fill(jsonData);
document.getElementById("el").innerHTML = jsonData[2].title3;
Dosometing(jsonData[0])
}
})
})
}
All JSON files have absolute 150kb. Problem is, sometimes when I run this code on website I get jsonData[0] undefinded and sometimes all load success. It means all data are not load properly. What im doing wrong ? There is any chance to write this code better to make sure all files are loaded properly ?
One issue is that even for small files it is not guaranteed, that the downloads finish in order.
It would be better to keep track of the finished download count with a separate variable:
function readData() {
var loadFile = function(filePath, done) {
var xhr = new XMLHttpRequest();
xhr.open("GET", filePath, true);
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
xhr.onload = function() { return done(this.responseText) }
xhr.send();
}
var finishedCount = 0;
address.forEach(function(file, i) {
loadFile(file, function(responseText) {
jsonData[i] = JSON.parse(responseText);
finishedCount++;
if(finishedCount === address.length) {
fill(jsonData[4]);
document.getElementById("el").innerHTML = jsonData[2].title3;
Dosometing(jsonData[0])
}
});
})
}
I'm trying to get a webworker to poll a web server interface on the same machine every second or so. Most articles I have read say to avoid setInterval and use setTimeout instead but I have yet to find an example that uses AJAX instead of Jquery.
The code I have so far is below:
(function poll() {
setTimeout(function() {
var xhr = new XMLHttpRequest();
xhr.onload = function() {
if (xhr.status === 200) {
responseObject = JSON.parse(xhr.responseText);
var newContent = '';
newContent += responseObject.cmd;
console.log(newContent);
}
}
xhr.open('GET', 'http://localhost:8194/screen_update/1000', true);
xhr.send(null);
setTimeout(poll, 1000);
}, 1000);
})();
The preferred output would be to poll the server each second which should in theory be more than adequate for the response to come through. I only want one request on the go at a time so if I end up with a request taking more than a second it just dumps the request (rather than queuing it) and issues a new request.
The above code polls okay but doesn't complete for 2 seconds so I've obviously got my setTimeout mixed up somewhere. Where do I correct this code?
I did just that a few days ago.. and while it may not be the most elegant, it works fine so far.
I have the worker handle the timeout / check interval, not the main JS. So I guess that's one more thing that the UI doesn't need to handle. Here is my worker code:
function checkStatus() {
console.log("statusCheck started");
var ajaxRequest;
try { ajaxRequest = new XMLHttpRequest(); // Opera 8.0+, Firefox, Safari
} catch (e) { try { // Internet Explorer Browsers
ajaxRequest = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) { try {
ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e) { // Something went wrong
console.error("AJAX not possible");
return false;
}
}
}
// Create a function that will receive data sent from the server
ajaxRequest.onreadystatechange = function() {
if(ajaxRequest.readyState == 4) {
self.postMessage(ajaxRequest.responseText);
var timer;
timer = self.setTimeout(function(){
checkStatus();
}, 1000);
}
}
ajaxRequest.open("GET", "/worker_statusCheck.php", true);
ajaxRequest.send(null);
}
this.onmessage = function(e){
checkStatus(); // the message comes in just once on pageLoad
};
Define a variable that determines if ajax finished or not. If function is called while ajax hasn't finished yet, you can exit the function and wait for the next call.
var stillWorking = false;
(function poll() {
if(stillWorking) return false;
stillWorking = true;
setTimeout(function() {
var xhr = new XMLHttpRequest();
xhr.onload = function() {
if (xhr.readyState == 4) stillWorking = false;
if (xhr.status === 200) {
responseObject = JSON.parse(xhr.responseText);
var newContent = '';
newContent += responseObject.cmd;
console.log(newContent);
}
}
xhr.open('GET', 'http://localhost:8194/screen_update/1000', true);
xhr.send(null);
setTimeout(poll, 1000);
}, 1000);
})();
You can call same function when you get response of AJAX. In this way no need to check that currently AJAX is in process or not.
function poll() {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange= function() {
if (xhr.status === 200) {
responseObject = JSON.parse(xhr.responseText);
var newContent = '';
newContent += responseObject.cmd;
console.log(newContent);
}
if (xhr.readyState == 4)
{
setTimeout(function(){ poll();},1000);
}
}
xhr.open('GET', 'http://localhost:8194/screen_update/1000', true);
xhr.send(null);
};
setTimeout(function(){ poll();},1000);
If you want to use onload callback then callback code should be
xhr.onload= function() {
if (xhr.status === 200) {
responseObject = JSON.parse(xhr.responseText);
var newContent = '';
newContent += responseObject.cmd;
console.log(newContent);
}
setTimeout(function(){ poll();},1000);
}
Because you are using HTML5 WebWorker, probably, you can use window.fetch which uses promises (https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API), I think that browser support is almost the same.
Here is an example:
((url, INTERVAL, configs) => {
const MAX_ERRORS = 4;
let errors = 0;
var poll = () => window.setTimeout(getData, INTERVAL);
function getData() {
return window
.fetch(url, configs)
.then(res => res.json())
.then(data => {
errors = 0;
poll();
return data;
})
.then((data) => {
console.log("new data available", data);
})
.catch(() => {
if(errors >= MAX_ERRORS) {
console.log("GIVING UP");
return;
}
errors += 1;
return poll();
})
;
}
return poll();
})("http://jsonplaceholder.typicode.com/posts/1", 1000, {
method: 'GET'
});
I'm trying to return a json object following an XMLHttpRequest get request, and I come up short. I think that might be because it is asynchronous, but I really can't put my finger on how to make it work. What am I doing wrong?
$(document).ready(function() {
var apiEndpoint = 'http://someapiendpoint.com/'
//Helpers
function sendRequest(_path) {
var results = {}
req = new XMLHttpRequest()
req.open('GET', apiEndpoint+_path)
req.onreadystatechange = function() {
if (this.readyState === 4) {
results = JSON.parse(this.response)
}
}
req.send()
return results
}
// Action
console.log(sendRequest('client1/'))
}); // end document ready
You should use this construction
function sendRequest(_path, cb) {
req = new XMLHttpRequest()
req.open('GET', apiEndpoint+_path);
req.onreadystatechange = function() {
if (this.readyState === 4) {
cb(JSON.parse(this.response));
}
else{
cb(null);
}
}
req.send();
}
// Action
sendRequest('client1/', function(result){
console.log(result);
})
For asynchronous calls you need to use call backs
Since you are already using jQuery you can do the following:
$(document).ready(function() {
var apiEndpoint = 'http://someapiendpoint.com/';
function sendRequest(path, callback){
$.get(apiEndpoint+path, function(response){
callback(JSON.parse(response));
}, json).fail(function(){
console.log('Failed');
});
}
sendRequest('client1/', function(json){
if(json){
console.log(json);
}
});
});
I am using the following JavaScript function object to handle requests to the server:
function Request(query, url) {
var that = this;
this.request = new XMLHttpRequest();
this.response = '';
this.set_response = function() {
if (that.request.readyState==4 && that.request.status==200) {
that.response = that.request.response;
}
}
this.request.onreadystatechange = this.set_response;
this.request.open('POST', url, false);
this.request.setRequestHeader("Content-type","application/x-www-form-urlencoded");
this.request.send(query);
}
With this object I can do something like:
var needed_data = new Request('data=needed', 'serverside.php').response;
Now the data I needed from the server is stored nicely in the needed_data variable (as a json encoded string).The problem is using a synchronous request in the main thread has been deprecated. I was hoping someone could help me figure out a way to achieve the same result whilst being standards compliant.
You will have to make it asynchronous.
//allow to pass a callback
function Request(query, url, callback) {
var that = this;
this.request = new XMLHttpRequest();
this.response = '';
this.set_response = function() {
if (that.request.readyState==4 && that.request.status==200) {
that.response = that.request.response;
callback && callback(that); //invoke the callback when data is available
}
}
this.request.onreadystatechange = this.set_response;
this.request.open('POST', url, true); //make the call async
this.request.setRequestHeader("Content-type","application/x-www-form-urlencoded");
this.request.send(query);
}
new Request('data=needed', 'serverside.php', function (request) {
console.log(request.response);
});
You should never be using a synchronous XMLHttpRequest in the same thread as the rest of your application.
function request(query, url, callback) {
var xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.onreadystatechange = function(){
if (xhr.readyState === 4 && xhr.status === 200 && callback) {
callback(xhr.responseText);
}
};
xhr.send(query);
return xhr;
}
request('data=needed', 'serverside.php', function(responseText){
var needed_data = JSON.parse(responseText);
});