I don't understand Ajax, Promise and XMLHttpRequest - javascript

this is my code:
const getXHR = (type, url) => {
let xhr = new XMLHttpRequest();
let p = new Promise(function(resolve, reject) {
xhr.onload = function() {
if(xhr.status >= 200 && xhr.status < 400) {
resolve(xhr.responseText);
} else {
reject(new Error(`Error. Code ${xhr.status}`))
}
};
xhr.onerror = function() {
reject(new Error(`Error. Problem with connection`));
}
});
xhr.open(type, url);
return {xhr, p};
};
export default {
get(url) {
var _getXHR = getXHR('GET', url),
xhr = _getXHR.xhr,
p = _getXHR.p;
xhr.send();
return p;
}
}
I don't understand why I have to use this code to get it to work:
var _getXHR = getXHR('GET', url),
xhr = _getXHR.xhr,
p = _getXHR.p;
instead of this:
var xhr = getXHR('GET', url).xhr,
p = getXHR('GET', url).p;
What is wrong with this? For me it is excatly the same lines of code. I would be grateful for any kind of help. Maybe someone has a link where I can find the answer?
Thank You

var xhr = getXHR('GET', url).xhr,
p = getXHR('GET', url).p;
Creates two requests, and then gets the xhr object of the first, and the promise of the second. The xhr request of the first is sent , that one of the promise is always in a pending state. So thats probably not working. May use some destructuring:
var { p, xhr } = getXHR('GET', url);
xhr.send();
return p;

Related

Trying to receive information from a JSON file and result is NULL

I'm trying to get data from a .json file and the result seems to be null.
I have been trying this and it has worked before with the exact same code and it worked perfectly
let ItemList = "./ItemList.json"
let request = new XMLHttpRequest();
request.open('GET', ItemList)
request.responseType = "json"
request.send()
request.onload = function () {
const Items = request.response
console.log(Items[0])
}
My questions are why is it the error Cannot read property '0' of null at XMLHttpRequest.request.onload and how can I fix it,
And my second question is, what is a better way to do this.
I have tried other things like referencing it further in such as console.log(Items.Test.TestCategory.TestProduct) and that still didn't work
My Json File
{
"AnotherTestProduct":[1,2], //this is what I'm trying to get
"Test": {
"TestCategory":{
"TestProduct": [0, "TestProduct", 5, 10, 0],//[Id, Name, Customer price, Stock price, inStock]
}
},
"Food": {
"Candy": {
"Chocolate-Bar": [0,""]
}
}
}
My code that worked perfectly
let PeopleNames = "./PeopleNames.json"
let request = new XMLHttpRequest();
request.open('GET', PeopleNames)
request.responseType = 'json';
request.send();
request.onload = function () {
const Names = request.response;
function Genders(Gender) {
if (Gender === 0)
return Names.firstNamesF[getRandomInt(999)] + " " +
Names.lastNames[getRandomInt(986)]
else if (Gender === 1) {
return Names.firstNamesM[getRandomInt(999)] + " " +
Names.lastNames[getRandomInt(986)]
}
}
Generator = Genders(getRandomInt(2));
function getRandomInt(max) {
return Math.floor(Math.random() * max);
}
var div = document.getElementById("name")
div.innerHTML = Generator
}
The Json that worked basically went
"firstNamesF": ["Emma", + 989 more strings]
"firstNamesM": ["Bob", + 989 more strings]
"lastNames": ["Wilson" + 970 more strings]
request.onload = function () {
const Items = request.response
console.log(Items[0])
}
You are trying to print an array value of your JSON object
request.onload = function () {
const Items = request.response;
const myFirstKey = Object.keys(Items)[0];
console.log(Items[myFirstKey])
}
Your JSON:
{
"AnotherTestProduct":[1,2], //this is what I'm trying to get
Your JSON is not valid. Invalid JSON will not parse. You can check it here:
https://jsonlint.com/
"TestProduct": [0, "TestProduct", 5, 10, 0],
has an extraneous comma at the end, should be
"TestProduct": [0, "TestProduct", 5, 10, 0]
One reason you may receive an error for one file and not another is likely due to the size. Bigger the size the longer it takes to process. If you increase the size of people.json do you get the same error?
If you are new to javascript it may sound weird but JS doesn't wait on code before executing the next line. So even though in the code you said open the file before onload, JS will try to onload before open finishes.
If you increase the size of people.json and you still get an error try wrapping the XHR request in a promise function.
const getData = (uri) => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('get', uri, true);
xhr.responseType = 'document';
xhr.onload = () => {
const status = xhr.status;
if (status == 200) {
// complete your function
// Think about resolve as return.
// It's not the same but it is.
resolve(SOMETHING)
} else {
reject(status);
}
};
xhr.send();
});
}
Then when you need to get something do this
const uri = './ItemList.json'
const getProducts = async (uri) => {
const data = await getData(uri);
// Do something here
}

Using promise to call Ajax without duplicating code

is this possible? I want to write an ajax function, that I do not want to duplicate it. Pass it different parameter which are locations to different files. Then use the promise to make them into one object. I would possible use the spread operator. is this possible.
var myFuncCalls = 0;
let promiseAjax = new Promise (function ( resolve,reject) {
//possibly use a for look to grab the number of times the loadDoc was called then call the same function and send it to may be an array?
function loadDoc(location) {
myFuncCalls++;
console.log("loadDoc was called :" + myFuncCalls);
var xyz = new XMLHttpRequest();
xyz.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
//console.log(this.responseText)
resolve(this.responseText);
}
};
xyz.open("GET", location, true);
xyz.send();
}
loadDoc("/_js/someitems.json");
loadDoc("/_js/someMoreItems.json");
})
// then grab all that stuff and make one single object using spread operators
promiseAjax.then(function (fromResolve){
// JSON.parse(fromResolve);
var newObj = JSON.parse(fromResolve);
console.log(newObj);
})
with Promise.all and Object.assign,
function loadDoc(location) {
return new Promise((resolve, reject) => {
var xyz = new XMLHttpRequest();
xyz.onreadystatechange = () => {
if (this.readyState == 4 && this.status == 200) {
resolve(JSON.parse(this.responseText));
} else {
// resolving with empty object to avoid breaking other fetch if one failed
resolve({});
}
};
xyz.open("GET", location, true);
xyz.send();
});
}
const loadDocs = (paths) => Promise.all(paths.map(path => loadDoc(path))
.then(results => {
// combine all result into single object
return Object.assign({}, ...results);
}));
// example
loadDocs([
"/_js/someitems.json",
"/_js/someMoreItems.json"
]).then(function(finalCombinedObject) {
// other logic here
});
Use Promise.all() to get the two calls together and so what ever you want with the array of the data you resolved.
function loadDoc(location) {
return new Promise (function ( resolve,reject) {
var xyz = new XMLHttpRequest();
xyz.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
resolve(this.responseText);
}
};
xyz.open("GET", location, true);
xyz.send();
});
}
const urls = ["/_js/someitems.json", "/_js/someMoreItems.json"]
Promise.all(urls.map(url=>loadDoc(url))).then(responses =>
console.log(responses);
)
I think the easiest thing would be to define async functions, which return promises and can be easily passed around and reused.
You can do something like:
async function loadFile(file) {
...
return {...fileJSON};
}
async function loadFiles() {
const file1JSON = await loadFile('file1');
const file2JSON = await loadFile('file2');
return {...file1JSON, ...file2JSON};
}
loadFiles().then((combinedJSON) => {
...
})
These functions can take arguments and be reused like any other function.
This kind of behavior can archived with Promise.all Promise.all white the use of async+await async and the use of more state of the art calls (fetch) makes the code looks cleaner
async function loadAll(docs) {
return Promise.all(docs.map(async doc => {
const result = await fetch('http://example.com/movies.json');
return result.json();
}));
}
(async function() {
const responses = await loadAll(["/_js/someitems.json", "/_js/someMoreItems.json"]);
console.log(responses);
})();
Note: await can only be used from an async function.
Note2: the code is untested
Yes, youcan send the URL, any parameters, even the type of AJAX call (POST, GET, etc), to the method, then use it to build the call. This way, you can reuse the same method to do anything and everything you need to do from your client with a "simple" method call.
All code in this Answer is copied from the below link.
https://medium.com/front-end-weekly/ajax-async-callback-promise-e98f8074ebd7
function makeAjaxCall(url, methodType)
{
var promiseObj = new Promise(function(resolve, reject)
{
var xhr = new XMLHttpRequest();
xhr.open(methodType, url, true);
xhr.send();
xhr.onreadystatechange = function()
{
if (xhr.readyState === 4)
{
if (xhr.status === 200)
{
console.log("xhr done successfully");
var resp = xhr.responseText;
var respJson = JSON.parse(resp);
resolve(respJson);
}
else
{
reject(xhr.status);
console.log("xhr failed");
}
}
else {console.log('xhr processing going on');}
}
console.log("request sent succesfully");
});
return promiseObj;
}
enter code here
document.getElementById('userDetails').addEventListener('click', function()
{
// git hub url to get btford details
var userId = document.getElementById("userId").value;
var URL = "https://api.github.com/users/"+userId;
makeAjaxCall(URL, "GET").then(processUserDetailsResponse, errorHandler);
});
You can even send it the callback method. I also send it a method to use for errors.
function makeAjaxCall(url, methodType, callback)
{
$.ajax(
{
url : url,
method : methodType,
dataType : "json",
success : callback,
error : function (reason, xhr){
console.log("error in processing your request", reason);
}
});
}
// git hub url to get btford details
var URL = "https://api.github.com/users/btford";
makeAjaxCall(URL, "GET", function(respJson)
{
document.getElementById("userid").innerHTML = respJson.login;
document.getElementById("name").innerHTML = respJson.name;
document.getElementById("company").innerHTML = respJson.company;
document.getElementById("blog").innerHTML = respJson.blog;
document.getElementById("location").innerHTML = respJson.location;
});

Chaining promises - correct methodology

I'm trying to create an excel add-in using Javascript that requires asynchronous functions return a JS promise to Excel and that the promise is resolved with the final value using the callback function. I am new to promises and have spent hours reading and testing this out with no success, and was hoping someone could help me understand what I'm doing wrong. Below is the code:
function TEST(num1) {
return new Promise(function (resolve) {
var corsproxy = "https://cors-anywhere.herokuapp.com/"
var apiurl = "https://randomapi.com/testapi"
var data = getData(corsproxy+apiurl).then(function(result){
console.log ("here it comes")
console.log(result.meta.status) /// This returns "Success"
return (result.meta.status) /// I need this to be resolved
})
console.log("last")
resolve(data)
})
};
/// Get JSON
function getData(url) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'json';
xhr.onload = function () {
try {
if (xhr.status === 200) {
resolve(xhr.response);
}
else if (xhr.status !== 200) {
reject({
error: 'Request failed. ' + xhr.response
});
}
} catch (e) {
reject({
error: e
});
}
};
xhr.send();
});
}
The second function getData is working as intended and returning a JS Object. What I'm trying to accomplish with the TEST function is:
Create a new promise - this needs to be resolved/returned as "Success"
Call the API data with getData(temporarily running through a proxy to bypass CORS erros)
Extract the meta.status value ("Success")
I've tried a number of different things but the current code write "last" and resolves an undefined data before the getData function completes. Changing the "return (result.meta.status)" to "resolve (result.meta.status)" also doesn't help.
Any assistance with what I'm doing wrong would be greatly appreciated.
function TEST(num1) {
var corsproxy = "https://cors-anywhere.herokuapp.com/"
var apiurl = "https://randomapi.com/testapi"
return getData(corsproxy+apiurl)
}
TEST(valofnum1).then(function(result){
console.log ("here it comes")
console.log(result.meta.status) /// This returns "Success"
return (result.meta.status) /// Needs to be resolved
})
that's how you chain promises. you don't resolve a promise within another promise,
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises
You can use async/await since ES6 that solves a lot of this headache by simplifying the chain process into using await statements when letting promises resolve. I've updated your code block to use it, take a look:
async function TEST(num1) {
return new Promise(function (resolve) {
var corsproxy = "https://cors-anywhere.herokuapp.com/"
var apiurl = "https://randomapi.com/testapi"
var result = await getData(corsproxy+apiurl);
resolve(result.meta.status)
})
};
/// Get JSON
function getData(url) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'json';
xhr.onload = function () {
try {
if (xhr.status === 200) {
resolve(xhr.response);
}
else if (xhr.status !== 200) {
reject({
error: 'Request failed. ' + xhr.response
});
}
} catch (e) {
reject({
error: e
});
}
};
xhr.send();
});
}
Changes made:
TESTis now an async function.
Rather chaining the resolved promise from getData and resolving in TEST, you simply await the response from getData and then resolve it.

Status Code 304

I am new to Javascript and server-side programming. I am trying to send a GET request to load an image from my blog: http://jsafaiyeh.github.io/img/suw_background.png
function imgLoad(url) {
return new Promise(function(resolve, reject) {
var request = new XMLHttpRequest({mozSystem: true});
request.open('GET', url);
request.responseType='blob';
request.onload = function() {
if (request.status >= 200 && request.status < 400) {
resolve(request.response);
} else {
reject(Error('Image did\'t load successfully; error code '+ request.statusText));
}
};
request.onerror= function() {
reject(Error('There was a network Error'));
};
request.send();
});
}
var body = document.querySelector('body');
var myImage = new Image();
imgLoad('http://jsafaiyeh.github.io/img/suw_background.png').then(function response() {
var imageURL = window.URL.createObjectURL(response);
myImage.src = imageURL;
body.appendChild(myImage);
}, function(Error) {
console.log(Error);
});
I get status code 304. However, the image still does not load onto the page. Any help would be appreciated.
You have wrong function signature. It should be like this:
imgLoad('http://jsafaiyeh.github.io/img/suw_background.png').then(function (response) {
var imageURL = window.URL.createObjectURL(response);
myImage.src = imageURL;
body.appendChild(myImage);
}, function(Error) {
console.log(Error);
});
Working demo on JSFiddle(at least in Chrome).
Instead of passing named function, called response you probably wanted response to be in argument list. So, instead of function response(), you need function (response). You didn't get error that response was undefined, because it actually was declared, but it wasn't expected result from promise, but function.

AJAX with callback functions

I am having trouble access data within an ajax connection. Not sure what is wrong with my code. It seems as though it never reaches the 2nd function. Any ideas?
function fetchgps(callback)
{
var url = "http://www.instamapper.com/api?action=getPositions&key=584014439054448247";
var myRequest = new XMLHttpRequest();
myRequest.onload = function(e) {xml_loaded(e, myRequest, callback);}
myRequest.open("GET", url);
myRequest.setRequestHeader("Cache-Control", "no-cache");
myRequest.setRequestHeader("wx", "385");
myRequest.send(null);
return myRequest;
}
function xml_loaded(event, request, callback)
{
if (request.responseText){
var obj = {error:false, errorString:null}
var data = myRequest.responseText;
collected=data.split(","); //parses the data delimited by comma and put data into array
obj.latitude = collected[4];
obj.longitude = collected[5];
callback(obj);
}
else
{
callback ({error:true, errorString:"XML request failed. no responseXML"}); //Could be any number of things..
}
}
function dealwithgps(obj)
{
lat = obj.latitude;
lon = obj.longitude;
document.write(lon);
document.write(lat);
}
fetchgps(dealwithgps);
Thats request.onreadystatechange instead of request.onload
function fetchgps(callback)
{
var url =
"http://www.instamapper.com/api?action=getPositions&key=584014439054448247";
var myRequest = new XMLHttpRequest();
// myRequest.onload = function(e) {xml_loaded(e, myRequest, callback);}
myRequest.onraedystatechange = function() { //onraedystatechange instead of onload!!
xml_loaded(myRequest, callback);
}
myRequest.open("GET", url);
myRequest.setRequestHeader("Cache-Control", "no-cache");
myRequest.setRequestHeader("wx", "385");
myRequest.send(null);
return myRequest;
}
function xml_loaded(request, callback) {
if(request.readyState === 4) {
//... only then do your processing
}
}

Categories

Resources