This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 2 years ago.
I'm having a problem with javascript code. I am trying to have dynamical chart, which will update on changing a select field with plotly.reshape function. I call an Ajax request based function inside of chart() function and i want it to wait for variable assignment and then draw a chart. I think i'm using async/await in a wrong place. Can u guys help me ? It is my very first js script, but i need it in project.
function chart(){
var x = Chart();
var X =x[0];
var Close=x[1];
var High=x[2];
var Low=x[3];
var Open=x[4];
console.log(X);
var trace1 = {
x: X,
close: Close,
decreasing: {line: {color: '#7F7F7F'}},
high: High,
increasing: {line: {color: '#17BECF'}},
line: {color: 'rgba(31,119,180,1)'},
low: Low,
open: Open,
type: 'ohlc',
xaxis: 'x',
yaxis: 'y'
};
var data = [trace1];
var layout = {
...
};
Plotly.newPlot('chart', data, layout);
}
function Chart(){
var data, date = [], price = [], open=[], Timestamp=[], High=[], Low = [];
let selectedItem = document.getElementById('currency-selector').value;
var url = `http://127.0.0.1:8000/${selectedItem}/`;
var x = new Array()
var requestURL = url; //URL of the JSON data
var request = new XMLHttpRequest({mozSystem: true}); // create http request
request.onreadystatechange = async function() {
if(request.readyState == 4 && request.status == 200) {
data = JSON.parse(request.responseText);
for (var i=0; i<data.length;i++) {
date.push(data[i].date)
price.push(data[i].close);
High.push(data[i].high);
open.push(data[i].Open);
Low.push(data[i].low);
}
//chart(date,price,High,Low,open);
}
await request.open('GET', requestURL, true);
request.send(); // send the request
}
return [date,price,High,Low,open];
}
I can't test this because of the set up, but this should work... and return things in the order you expect them to be.
this is using the fetch api, which is generally much cleaner than the xmlhttp request api.
but so you know, async is the label for the function that contains await. and .then() is how to order things in a callback like that... the value that is awaited will execute each then first before returning the awaited value.
async function Chart(){
let date = [], price = [], open=[], Timestamp=[], High=[], Low = [];
let selectedItem = document.getElementById('currency-selector').value;
let url = `http://127.0.0.1:8000/${selectedItem}/`;
let requestURL = url; //URL of the JSON data
return await fetch(requestURL)
.then(res=>res.json())
.then(data=>{
data.forEach(x=>{
date.push(x.date)
price.push(x.close);
High.push(x.high);
open.push(x.Open);
Low.push(x.low);
})
})
.then(()=>{
return [date,price,High,Low,open];
})
}
Related
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 10 months ago.
I'm trying to access the NHL API for a personal project. I was getting CORS request errors and found the below solution to access all the different sites (some worked prior, and some didn't).
I'm passing through a link and based one the outputs it seems to be accessing the data on the page and displaying it in both JSON and string format. The only problem is I can't return any of that data out of the function as an object or otherwise.
Last 2 logs show undefined, and the 2 within the function output the data I want. Am I missing something obvious or is there a deeper issue with the website access?
Thanks.
function getJSON(url){
let request = new XMLHttpRequest();
request.open('GET', url);
request.onload = function () {
// Begin accessing JSON data here
let data = JSON.parse(this.response);
if (request.status >= 200 && request.status < 400) {
//Start writing function here
console.log(data);
console.log(JSON.stringify(data));
let data2 = JSON.stringify(data)
return data2
} else {
console.log('error');
}
}
request.send();
}
let data1 = getJSON("https://statsapi.web.nhl.com/api/v1/people/8476459/stats?stats=statsSingleSeason&season=20212022")
console.log(data1);
console.log(JSON.stringify(data1));
You are doing asynchronous call, which means you return the value before the response even come back, that's why data1 is undefined
You can see this post for more information.
For your case, you can wrap the request with Promise
async function getJSON(url) {
return new Promise(function (resolve, reject) {
let request = new XMLHttpRequest();
request.open('GET', url);
request.onload = function () {
// Begin accessing JSON data here
let data = JSON.parse(this.response);
if (request.status >= 200 && request.status < 400) {
//Start writing function here
let data2 = JSON.stringify(data)
return resolve(data);
} else {
return reject('error')
}
}
request.send();
}
)
}
async function main() {
let data1 = await getJSON("https://statsapi.web.nhl.com/api/v1/people/8476459/stats?stats=statsSingleSeason&season=20212022")
console.log(data1);
console.log(JSON.stringify(data1));
}
main();
A better way is to use Fetch API
async function getJSON(url) {
const data1 = await fetch(url).
then(response => response.json());
return data1;
}
async function main() {
const result = await getJSON('https://statsapi.web.nhl.com/api/v1/people/8476459/stats?stats=statsSingleSeason&season=20212022');
console.log(result);
}
main();
I am using sheetJS in order to manipulate excel sheets. My goal is to extract the value of a cell and store it in an array as raw data for later statistical analysis and graphing.
Here is what the function looks like:
function getSheetData()
{
let rawData = [];
/* set up XMLHttpRequest */
var url = "test.xlsx";
var oReq = new XMLHttpRequest();
oReq.open("GET", url, true);
oReq.responseType = "arraybuffer";
oReq.send();
oReq.onload = function (e) {
var arraybuffer = oReq.response;
/* convert data to binary string */
var data = new Uint8Array(arraybuffer);
var arr = new Array();
for (var i = 0; i != data.length; ++i) arr[i] = String.fromCharCode(data[i]);
var bstr = arr.join("");
/* Call XLSX */
var workbook = XLSX.read(bstr, {
type: "binary"
});
/* DO SOMETHING WITH workbook HERE */
var sheet_name_list = workbook.SheetNames;
// var worksheet;
sheet_name_list.forEach(function(y) { /* iterate through sheets */
var worksheet = workbook.Sheets[y];
for (z in worksheet) {
/* all keys that do not begin with "!" correspond to cell addresses */
if(z[0] === '!') continue;
// console.log(z + " = " + JSON.stringify(worksheet[z].v));
rawData.push(worksheet[z].v);
}
});
/* Get worksheet */
// console.log(XLSX.utils.sheet_to_json(worksheet, {
// raw: true
// }));
console.log("raw data = " + rawData);
}
// console.log(rawData);
return rawData;
}
The console.log defined as 'raw data' shows all the numbers in one array just how I need it. However, the array named "rawData" returns as undefined by the end of the function.
I am calling the function here:
window.onload = function()
{
const data = getSheetData();
const BenfordTable = calculateBenford(data);
printAsTable(BenfordTable);
printAsGraph(BenfordTable);
}
I get data as an empty array
I have included a picture of the browser window
screenshot of console results in google chrome
data is an empty array because getSheetData() is an asynchronous function - that is to say, you are making an XMLHttpRequest call from within it. If you put console logs within your onload handler and right before your return statement, you will see that the latter runs first. The issue is that when your function returns, the call to the server will not have yet returned.
There are a few different ways of writing asynchronous code, but I think you should start off by passing a callback function to getSheetData() which will be called from within your onload handler. This callback function will be what handles rawData.
Here's roughly how you might do this. I have omitted some of the existing code for brevity, but obviously you will need it.
function getSheetData(callback)
{
let rawData = [];
// ...other code
oReq.onload = function (e) {
var arraybuffer = oReq.response;
// ...other code
callback(rawData); // <-- add this
}
// no need to return anything!
// return rawData;
}
window.onload = function()
{
getSheetData(function (data) {
const BenfordTable = calculateBenford(data);
printAsTable(BenfordTable);
printAsGraph(BenfordTable);
});
}
There are other things you could use to write such code, such as Promises, but that's probably something else to look into. We're also not doing any error handling here, which is also an important concept. The main takeaway here is that you are only handling the rawData once the call to the server has completed.
So I'm having trouble with getting a VAR in a function to be global, I have tried the following resources:
What is the scope of variables in JavaScript?
My previous question was marked as a duplicate but after reviewing the link above it did not help with my issue.
Here is my previous question:
So I'm using OpenTok to create a online conferencing tool and need to grab the session details from an API on a different server. I've created a php script on the other server that grabs session information based on the session id provided by a URL parameter. I know that the php script and most of the JavaScript is working correctly because when I console.log data from the parsed JSON it prints the correct information. However when I try to put the variables into the credentials area I get the following error:
ReferenceError: thesession is not defined
Here is the code used to get the JSON from a PHP script on a separate server:
var url_string = window.location.href;
var url = new URL(url_string);
var session = url.searchParams.get("s");
if (session == '') {
window.location.replace("http://www.google.com");
}
var getJSON = function(url, callback) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'json';
xhr.onload = function() {
var status = xhr.status;
if (status === 200) {
callback(null, xhr.response);
} else {
callback(status, xhr.response);
}
};
xhr.send();
};
getJSON('http://192.168.64.2/api/meeting/?uid=' + session,
function(err, data) {
if (err !== null) {
console.log('Error');
}
var thesession = data.sessionID;
var thetoken = data.token;
console.log(thesession);
console.log(thetoken);
});
let otCore;
const options = {
credentials: {
apiKey: "####",
sessionId: thesession,
token: thetoken
},
And here is a screenshot of the console:
The top console log is "thesession" and the second console log is "thetoken". I have tried looking up the error but can't quite find one with the same usage as mine.
The desired outcome would be that I could using the data from the parsed JSON and use the result as the credentials e.g. data.sessionID which is bound the the VAR thesession.
I know this might be a scope issue, but I'm not sure how I could alter the code to make it work as intended.
Any help would be much appreciated, this one has really got me stumped :)
How would I alter the scope to get the desired function? I have reviewed the link that was given on the previous question, but this didn't help me with my issue.
var thesession = data.sessionID;
Is defined within its execution context, which is the callback function you've passed to getJSON.
One step in the right direction is to reverse the assignment. Assign 'thesession' to the options object within the scope where 'thesession' exists.
const options = {
credentials: {
apiKey: "####",
sessionId: null,
token: thetoken
}
};
getJSON('http://192.168.64.2/api/meeting/?uid=' + session,
function(err, data) {
if (err !== null) {
console.log('Error');
}
var thesession = data.sessionID;
var thetoken = data.token;
console.log(thesession);
console.log(thetoken);
options.credentials.sessionId = thesession;
});
However, it's important to realize that your program is not going to wait for this assignment. It will send the getJSON request, and then continue processing. Your options object won't have a sessionId until the getJSON call finishes and its callback has been invoked.
This would be a good opportunity to delve into Promises, which will help you better understand how to handle the non-blocking nature of javascript.
Your problem is that this line var thesession = data.sessionID is scoped within the function function(err, data) { ... }. In order to allow two functions to use the same variable, you need to make sure that the variable isn't declared somewhere they don't have access to.
It's the difference between this:
function func1() {
var x = 3
}
function func2() {
console.log(x)
}
func1();
func2();
and this:
var x;
function func1() {
x = 3
}
function func2() {
console.log(x)
}
func1();
func2();
Similarly, if you declare var thesession; at the start of your script (or at least outside that other function) then just set it with thesession = data.sessionID, your final part will have access to your variable thesession.
Edit
In context:
var url_string = window.location.href;
var url = new URL(url_string);
var session = url.searchParams.get("s");
var thesession;
var thetoken;
if (session == '') {
window.location.replace("http://www.google.com");
}
var getJSON = function(url, callback) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'json';
xhr.onload = function() {
var status = xhr.status;
if (status === 200) {
callback(null, xhr.response);
} else {
callback(status, xhr.response);
}
};
xhr.send();
};
getJSON('http://192.168.64.2/api/meeting/?uid=' + session,
function(err, data) {
if (err !== null) {
console.log('Error');
}
thesession = data.sessionID;
thetoken = data.token;
console.log(thesession);
console.log(thetoken);
});
let otCore;
const options = {
credentials: {
apiKey: "####",
sessionId: thesession,
token: thetoken
},
As a side-note - I'd also recommend not using var and instead just using let of const, depending on if you want your variable to be mutable or not.
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
How do I promisify native XHR?
(6 answers)
Closed 3 years ago.
i have some problem with ordering my functions.
i have 2 function. Two of them to http requests.
what a scenario?
on first request i get some response and in this response i'm getting some string and set it to some variable.
on second request i'm adding this string to my request url.
but my second request working before then in first request response when i set my variable.
because of that i'm getting undefined value of my variable.
I understand this is sync, async problem but how i can resolve this issue ?
it's my variable which i will add end of the second request url.
var urlString = '';
this is my first request:
var requestone = new XMLHttpRequest();
requestone.onload = function () {
if (requestone.status >= 200 && requestone.status < 300) {
var data = requestone.response;
data = JSON.parse(data);
urlString = data.Key
} else {
console.log('fail')
}
};
requestone.open('GET', 'apiurl');
requestone.send();
this is my second request:
var requesttwo = new XMLHttpRequest();
requesttwo.onload = function () {
if (requesttwo.status >= 200 && requesttwo.status < 300) {
var data = requesttwo.response;
} else {
console.log('fail')
}
};
requesttwo.open('GET', 'apiurl' + urlString);
requesttwo.send();
You can do this in 2 ways, using promise, or integrate the second request inside the request1:
var requestone = new XMLHttpRequest();
requestone.onload = function () {
if (requestone.status >= 200 && requestone.status < 300) {
var data = requestone.response;
data = JSON.parse(data);
requesttwo.open('GET', 'apiurl' + data.Key);
requesttwo.send();
} else {
console.log('fail')
}
};
requestone.open('GET', 'apiurl');
requestone.send();
I am trying to run multiple Ajax functions on load in a single page which will get data from two different php pages. Both the Ajax functions will then print the retrieved data onto the page from which the ajax function was called. The problem I encountered was that the last function call which I make from the Ajax overrides the first function call, and so only the second function result is showed.
The code for one of the Ajax function (since both of the are very similar to each other):
function favorite_track_request(str){
switch(str){
case 'next_track':
var feed = 'require_fav_track_info';
var offset = track_currentOffset + 5;
if(offset > max_track_range){
offset -= 5;
}
break;
case 'prev_track':
var feed = 'require_fav_track_info';
var offset = track_currentOffset - 5;
if(offset < 0){
offset = 0;
}
break;
default:
var feed = 'require_fav_track_info';
var offset = 0;
}
request = new ajaxRequest()
request.open("GET", "scripts/"+feed+".php?offset="+offset, true)
request.onreadystatechange = function(){
if(this.readyState == 4){
if(this.status == 200){
if(this.responseText != null){
if(request.responseText){
document.getElementById('fav_tracks').innerHTML = request.responseText;
}
}else alert("No data recieved");
}else {
alert("Ajax error: "+this.statusText);
}
}
}
request.send(null);
track_currentOffset = offset;
}
This ajax would then print to <div id="fav_tracks"></div>, however this gets overridden because another call (similar to the Ajax above) is made and that overrides the previous one. Is there any way to stop this?
I built a data handler "class" to manage just such a thing. You are right, the one overrides the other. I haven't investigated it, but it's probabably because your are re-assigning the onEvent that AJAX uses.
Below is the class I built (I know, it's not JQuery... it works). What it does is uses timeouts to "know" when to fire the second and third async request. There probably is a JQuery function that does the same thing.
You would call this by using the below for each AJAX call (giving each call a unique var name):
dataHandler = new DataHandler("[name of datafile to call]");
dataHandler.query['[myQueryName]'] = 'myValue' //this is an Object used to build a query string, if needed, so use as many data pairs as you need
dataHandler.asynchronous(myOnReadyStateChangeFN);//put the fn you want to use for readystatechange as a reference... do not includ the ()
Here's the "class":
function DataHandler(dataFile){
this.dataFile = dataFile;
dataInProgress = false;
this.query = new Object();
this.asynchronous = function(fn){
var thisFunction = this.asynchronous
var rand = Math.floor(Math.random()*100001);
var query, timeOutFunctionString;
if(this.dataInProgress){
timeOutFunctionString = callingObjectName+".asynchronous("+fn+")";
this.thisTimeout = setTimeout(timeOutFunctionString,500);
}else{
dataInProgress = true;
this.assignRequestObject.xmlHttp.onreadystatechange = function () {
fn();
dataInProgress = false;
return;
};
}
query = this.dataFile + '?r=' + rand;
for (var key in this.query) query = query + '&' + key + '=' + this.query[key];
//console.info("DataHandler.asynchronous\nquery = "+query+'\nthis.dataFile = ' + this.dataFile);
this.assignRequestObject.xmlHttp.open('GET', query, true);
this.assignRequestObject.xmlHttp.send(null);
};
this.AssignRequestObject = function() {
try { this.xmlHttp = new XMLHttpRequest() } catch (e) {
try { this.xmlHttp = new ActiveXObject("Msxml2.XMLHTTP") } catch (e) {
try { this.xmlHttp = new ActiveXObject("Microsoft.XMLHTTP") } catch (e) {
alert("Your browser does not support AJAX!");
return false
}
}
}
};
this.assignRequestObject = new this.AssignRequestObject();
};