I'm trying to make a pokedex for a school project, and am using JS for doing so. I found this api called pokeapi, and since it uses JSON, I thought of just getting the data in the page and returning a json of it, however when I try to use the method I created to do a request in a for loop, it doesn't seem to work, only working on the last element of the for loop:
let request = new XMLHttpRequest();
const getJSON = (link, action) => {
request.open("GET", link);
request.send();
request.onload = () => {
if (request.status === 200) {
let json = JSON.parse(request.response);
action(json);
} else {
console.log(`e:${request.status} ${request.statusText}`);
}
}
}
let counter = 1;
getJSON("https://pokeapi.co/api/v2/pokedex/1/", (json) => {
json.pokemon_entries.forEach((poke_entry) => {
getJSON(poke_entry.pokemon_species.url, (poke_sp) => {
console.log(poke_sp);
//console loggin poke_sp only shows one console log, the last member of `json.pokemon_entries`
})
});
});
This is because you have created a single XHR object.
Every time you make a request with it, you cancel the previous request.
Create a new XHR object (inside the getJSON function) for each request.
i.e. swap the order of
let request = new XMLHttpRequest();
and
const getJSON = (link, action) => {
Related
I have a typescript service that loads base data by htpp requests from a server. There are several requests for several data, arranged in order from undependent data to data depending on data, for which loading ist started bevor. Because the asynchronous http requests it is not guaranteed, that loading data by a request (e. g. customers in the following example) has finished, bevor loading the data that depends on it starts (e. g. devices in the following example) and i can not refer on loading devices to loaded customers. But i would like, that laoding the dependent data (devices) starts, after loading the data refereing to (customers) has finished. I think i need to use promises, but how to implement concrete in the following code situation?
export class BaseDataService
{
customers: Customer[] = new Array();
devices: Device[] = new Array();
// Loads the base data arranged in order from undependent data to data dependent data
loadBaseData() {
this.loadCustomers();
// this operation should be started after loading customers has finished (the response json
//is parsed to an array of customers at set to the instance variable of this service class)
this.loadDevices();
}
// Loads the customers and adds them to the collection of customers.
// Also puts the response json string of the customers to the local storage.
loadCustomers() {
console.log("Load customers");
var requestURL = 'https://myurl/kunden_json_user_extern.php';
var auth = window.localStorage.getItem('auth');
var requestparam = "auth="+auth;
var request = new XMLHttpRequest();
request.open('POST', requestURL);
request.setRequestHeader("content-type", "application/x-www-form-urlencoded");
request.send(requestparam);
request.onload = () => {
if (request.status === 200){
console.log("Load customers response");
// gets as json string with the customers array
var response = request.response;
// parses the json string of customers to an array of customers objects
this.customers = this.parseCustomers(response);
console.log("Load customers complete");
}
else if (request.status === 401){
alert("unautorized");
}
else {
// lade saved items aus local storage
alert("unerwarteter Fehler");
}
}
}
// Loads the devices and adds them to the collection of devices.
// Also puts the response json string of the devices to the local storage.
loadDevices() {
console.log("Load devices");
var requestURL = 'https://myurl/geraete_json_user_extern.php';
var auth = window.localStorage.getItem('auth');
var requestparam = "auth="+auth;
var request = new XMLHttpRequest();
request.open('POST', requestURL);
request.setRequestHeader("content-type", "application/x-www-form-urlencoded");
request.send(requestparam);
request.onload = () => {
if (request.status === 200){
console.log("Load devices response");
var response = request.response;
window.localStorage.setItem('devicesresponse', response);
this.devices = this.parseDevices(response);
console.log("Load devices complete");
}
else if (request.status === 401){
alert("unautorized");
}
else {
// lade saved items aus local storage
alert("unerwarteter Fehler");
}
}
}
// Parses a json string formatted like the response of customers to an array of customers
parseCustomers(customersjson: string)
{
let customerarray = JSON.parse(customersjson);
let customers: Customer[] = [];
for (let i = 0; i < customerarray.length; i++) {
let customer: Customer = new Customer();
customer.setId(customerarray[i]['id']);
customer.setName(customerarray[i]['name']);
customer.setPlace(customerarray[i]['ort']);
customer.setStreet(customerarray[i]['strasse']);
customers[i] = customer;
}
return customers;
}
// Parses a json string formatted like the response of devices to an array of devices
parseDevices(devicesjson: string)
{
let devicearray = JSON.parse(devicesjson);
let devices: Device[] = [];
for (let i = 0; i < devicearray.length; i++) {
let device = new Device();
device.setId(devicearray[i]['geraet_id']);
device.setSerialnumber(devicearray[i]['geraet_seriennummer']);
device.setDescription(devicearray[i]['geraet_bezeichnung']);
// here i would like to refer to a prevoiusly loaded customer
//device.setCustomer(this.baseDataService.getCustomer(devicearray[i]['geraet_kunde_id']);
device.setLocation(devicearray[i]['geraet_standort']);
devices[i] = device;
}
return devices;
}
}
You can pass a callback to any of 'load' functions, so:
This code:
loadBaseData() {
this.loadCustomers();
// this operation should be started after loading customers has finished (the response json
//is parsed to an array of customers at set to the instance variable of this service class)
this.loadDevices();
}
Change to this code:
loadBaseData() {
this.loadCustomers(() => {
this.loadDevices();
});
}
And call this callback inside your request.onload:
// Loads the customers and adds them to the collection of customers.
// Also puts the response json string of the customers to the local storage.
loadCustomers(callback) { // <--- this is a callback passed to function
// ... many code here ...
request.onload = () => {
// ... many code here ...
console.log("Load customers complete");
if (typeof callback === 'function') callback(); // <--- here we call it
}
// ... many code here ...
You can make a synchronous ajax call like below. So it waits for the response before executing the next statements.
xhr.open("POST",url,false);
Preface: I'm a novice at JS, have no formal training in it, and usually make things on the fly by researching what I am trying to do. That failed this time.
I am currently trying to make a short JS script that will serve as a bookmarklet. The intent is to leverage the Tinder API to show users of Tinder some of the profile pictures of users who liked them, normally available with the Gold Feature.
Currently, it looks like this:
var stringz;
var xhr = new XMLHttpRequest();
var tokez = localStorage.getItem("TinderWeb/APIToken");
var url = "https://api.gotinder.com/v2/fast-match/teasers?locale=en";
xhr.withCredentials = true;
xhr.open("GET", url);
xhr.setRequestHeader("accept", "application/json");
xhr.setRequestHeader("content-type", "application/json; charset=utf-8");
xhr.setRequestHeader("x-auth-token", tokez);
xhr.setRequestHeader("tinder-version", "2.35.0");
xhr.setRequestHeader("platform", "web");
xhr.send();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
stringz = xhr.responseText;
return stringz;
}
};
//Turn the xhr response into a JSON string
var jasonstring = JSON.parse(stringz);
//Grab the URLs
var jasonstrung = jasonstring.data.results.map(x => x.user.photos.map(y => y.url));
//Turn the URLs into a nicely formatted JSON string
var jason = JSON.stringify(jasonstrung, null, 4);
//See what we got
console.log(jason);
The reason I am doing both JSON.parse and JSON.stringify is that the returned data from the xhr is a text string formatted like JSON but it isn't actually JSON yet so I have to parse it in order to grab the pieces I want, then format them after so they aren't a goopy block (although the stringify part isn't super necessary)
On the first run of this in the Chrome Dev Console, it spits out the following:
VM5418:1 Uncaught SyntaxError: Unexpected token u in JSON at position 0
at JSON.parse (<anonymous>)
at <anonymous>:18:24
My assumption as to why it does this is because stringz is not yet "filled up" and returns as "undefined" when JSON.parse tries to cut through it.
However, once the script completes, if one were to type console.log(stringz), the expected string appears! If one runs the entire script 2x, it prints out the final desired dataset:
[
[
"https://preview.gotinder.com/5ea6601a4a11120100e84f58/original_65b52a4a-e2b2-4fdb-a9e6-cb16cf4f91c6.jpeg"
],
[
"https://preview.gotinder.com/5a4735a12eced0716745c8f1/1080x1080_9b15a72b-10c3-47c6-8680-a9c1ff6bdbf7.jpg"
],
[
"https://preview.gotinder.com/5e8d4231370407010088281b/original_adb4a1e3-06c0-4984-bca1-978200a5a311.jpeg"
],
[
"https://preview.gotinder.com/5ea77de583887d0100f385b8/original_af32971d-6d80-4076-a0f8-92ab54f820b3.jpeg"
],
[
"https://preview.gotinder.com/5bf7a1a29c0764cc3409bb02/1080x1350_c9784773-b937-4564-8c96-1a380832fdab.jpg"
],
[
"https://preview.gotinder.com/5d147c0560364e16004bcf5e/original_bf550230-baba-4d70-8c75-da64a9ce1b6c.jpeg"
],
[
"https://preview.gotinder.com/5c9ca2c2c8a4501600a979aa/original_915f4c0f-eb58-4283-bc58-00fdadc3c33c.jpeg"
],
[
"https://preview.gotinder.com/541efb64f5d81ab67f4b599f/original_7f11dea4-41c8-4e9c-8c7a-0c886484a076.jpeg"
],
[
"https://preview.gotinder.com/5a8b56376c220c1f5d8b43d9/original_7c19a078-8bd7-48f9-8e30-123b8f937814.jpeg"
],
[
"https://preview.gotinder.com/5d0c18341ea6e416002bfb1d/original_41d203ce-d116-4714-a223-90ccfd928ff2.jpeg"
]
]
Is there any way to make this thing work in one go (bookmarklet style)? setTimeout doesn't work unfortunately, assuming it is a problem in terms of taking too long to fill "stringz" before I use JSON.parse on it.
Thank you!
The problem is coming from that XHR makes your function asynchronous: it sends a request and the response arrives later - during that time your next (and next, and next,....) lines of code are executed.
You have to start your JSON string transformation when the response has already arrived - that means you should place your code xhr.onreadystatechange (I had to comment out a lot of things so the snippet works):
var stringz;
var xhr = new XMLHttpRequest();
// var tokez = localStorage.getItem("TinderWeb/APIToken");
// var url = "https://api.gotinder.com/v2/fast-match/teasers?locale=en";
var url = "https://jsonplaceholder.typicode.com/posts";
// xhr.withCredentials = true;
xhr.open("GET", url);
// xhr.setRequestHeader("accept", "application/json");
// xhr.setRequestHeader("content-type", "application/json; charset=utf-8");
// xhr.setRequestHeader("x-auth-token", tokez);
// xhr.setRequestHeader("tinder-version", "2.35.0");
// xhr.setRequestHeader("platform", "web");
xhr.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
// the response arrives here
stringz = this.responseText;
// start your JSON transformation when the
// response arrives
jsonTransform(stringz)
return stringz;
}
};
xhr.send();
// this part of code will be executed synchronously - this
// doesn't wait until your response arrives
var synchronous = 'this will be logged before response arrives'
console.log(synchronous)
function jsonTransform(stringz) {
//Turn the xhr response into a JSON string
var jasonstring = JSON.parse(stringz);
//Grab the URLs
// var jasonstrung = jasonstring.data.results.map(x => x.user.photos.map(y => y.url));
//Turn the URLs into a nicely formatted JSON string
// var jason = JSON.stringify(jasonstrung, null, 4);
//See what we got
const jason = JSON.stringify(jasonstring)
console.log(jason);
}
Another method
I suggest you use fetch() instead of xhr - with xhr you have to take care of everything - fetch() is quite new, with a Promise based syntax (you'll be meeting that a lot, if you work with APIs)
More on fetch():
https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
const url = "https://jsonplaceholder.typicode.com/posts";
// fetch returns a Promise, so you can use
// .then, .catch, .finally - very handy!
fetch(url)
.then(resp => {
return resp.json()
})
.then(json => {
// you have the json formatted response here:
console.log(json)
})
.catch(err => {
console.log(err)
})
You could use fetch() with the friendly async-await syntax, that makes your code feel synchronous:
const url = "https://jsonplaceholder.typicode.com/posts";
// await can only be placed in an async function!
async function fetchAPI(url) {
// try-catch block to handle errors of the fetch()
try {
const response = await fetch(url)
const json = await response.json()
console.log(json)
} catch (err) {
console.log(err)
}
}
fetchAPI(url)
I am working on a small script that is supposed to take information from a JSON file and use that info to populate a table in my webpage. I am writing a script from a Youtube tutorial here, below is the code I have as given in the tutorial:
<script type="text/javascript">
const rankingsBody = document.querySelector("#rankings-table > tbody");
function loadRankings() {
const request = new XMLHttpRequest();
request.open("get", "data/rankings.json");
request.onload = () => {
try {
const json = JSON.parse(request.responseText);
populateRankings(json);
} catch (e) {
console.warn("Could not load rankings");
}
};
request.send();
}
function populateRankings (json) {
console.log(json);
}
console.log(request);
</script>
Now, the console.log() function at the end of the script is supposed to print the contents of the JSON file to the console, upon checking in Firefox, it does not print and throws the following error error:
ReferenceError: request is not defined
And when I try to run the loadRankings function in the console, it says it is undefined, So, I guess its not returning the data as expected because it hasn't been defined?
I am not really sure what the problem could be, could you help me to figure this one out? Thanks in advance guys.
You declared request in loadRankings(). Then you tried to print it outside of the context where it exists with console.log(request); const declarations are not hoisted, so you get a reference error. Either move the declaration to the global scope like this so it's visible to your console.log() statement:
const rankingsBody = document.querySelector("#rankings-table > tbody");
let globalRequest;
function loadRankings() {
const request = new XMLHttpRequest();
// Expose the most recent request in the global variable
globalRequest = request;
request.open("get", "data/rankings.json");
request.onload = () => {
try {
const json = JSON.parse(request.responseText);
populateRankings(json);
} catch (e) {
console.warn("Could not load rankings");
}
};
request.send();
}
function populateRankings (json) {
console.log(json);
}
console.log(globalRequest);
or just remove the console.log() statement entirely.
The Ajax request is being sent continuously and I am not sure why
placing return false in multiple areas of the code.
//when user clicks on create channel
document.querySelector('#add-channel').onclick = () => {
// pop up modal
modal1.style.display = "block";
// Initialize new request
document.querySelector('#form1').onsubmit = () => {
const request = new XMLHttpRequest();
const chatroom = document.querySelector('#chatroom').value;
const username = localStorage.getItem('name');
request.open('POST', '/add_chatroom');
// Callback function for when request completes
request.onload = () => {
// Extract JSON data from request
const data = JSON.parse(request.responseText);
// Update the result div
if (data.success) {
// get the data that was returned.
// add it back to the list
document.querySelector('#contacts').append(li);
}
else {
alert('chatroom already exists');
}
}
// add data to send to the server
const data = new FormData();
data.append('chatroom', chatroom);
data.append('username', username);
// Send request
request.send(data);
return false;
};
};
The post request won't stop everytime I click ok even though the event should only trigger when the form with id #form1 is submitted. It triggers even when i click ok for the alert.
I ended up changing the browser from Firefox to Microsoft edge to solve the onsubmit re-running issue, why this works is beyond my knowledge.
When I call function CallMe, I get a result from service but my HTML element has text undefined, because of service is still loading data. I try async, await on Test() but no results. I want this to be pure JS. What I do wrong?
CallMe(){
document.getElementById('testId').InnerHTML = Test();
}
function Test(){
var request = new XMLHttpRequest(); // Create a request variable and assign a new XMLHttpRequest object to it.
request.open('GET', 'https://some service'); // Open a new connection, using the GET request on the URL endpoint
request.send();
request.onload = async function () {
var data = await JSON.parse(this.response);
return data[0][0][0];
}
}
function CallMe() {
var request = new XMLHttpRequest(); // Create a request variable and assign a new XMLHttpRequest object to it.
request.open('GET', 'https://some service'); // Open a new connection, using the GET request on the URL endpoint
request.send();
request.onload = async function () {
var data = JSON.parse(this.response);
document.getElementById('testId').InnerHTML = data // depending on your response targert your desired property.
}
}
just do one method and when the call returns a response assign the value to your testId div
Rather than using XMLHttpRequest to make ajax call I recommend using fetch api as it uses Promises, which enables a simpler and cleaner API, avoiding callback hell and having to remember the complex API of XMLHttpRequest. (ref: https://developers.google.com/web/updates/2015/03/introduction-to-fetch).
Using fetch api the your code in pure js will look like below.
fetch('https://some service')
.then( res => res.json() )
.then(response => {
document.getElementById('testId').innerHTML = data[0][0][0];
})
.catch(error => console.error('Error:', error));