I'm creating a javascript library for a project im doing that makes rest calls based on params you feed it. This is the second day of the project and I'm looking for advice. If I set my request to be async it returns my request but i can't access the object value, if I set it to false in the call it returns an object.
I read the stack articles on async js request, and I can't seem to wrap my head around call backs and promises.
this works:
request.open("DELETE", url, false);
this doesn't:
request.open("DELETE", url, true);
(function(window){
function defineCynergi(){
var Cynergi = {};
Cynergi.get = function(url){
var request = makeHttpObject();
request.open("GET", url, false);
request.send(null);
return JSON.parse(request.responseText);
}
Cynergi.delete = function(url){
var request = new XMLHttpRequest();
request.open("DELETE", url, false);
request.setRequestHeader('Accept', 'application/localhost.com:3000+json; version=1');
request.send();
deleteStatus = request.statusText;
return deleteStatus;
}
Cynergi.insert = function(url, data){
var request = new XMLHttpRequest();
request.open("POST", url, false);
request.setRequestHeader('Accept', 'application/localhost.com:3000+json; version=1');
request.send(JSON.stringify(data));
sentStatus = request.statusText;
return sentStatus;
}
Cynergi.update = function(url, data){
var request = new XMLHttpRequest();
request.open("PATCH", url, false);
request.setRequestHeader('Accept', 'application/localhost:3000+json; version=1');
request.send(JSON.stringify(data));
updateStatus = request.statusText;
console.log(request);
return updateStatus;
}
return Cynergi;
}
if(typeof(Cynergi) === 'undefined'){
window.Cynergi = defineCynergi();
}
})(window);
function makeHttpObject() {
try {return new XMLHttpRequest();}
catch (error) {}
try {return new ActiveXObject("Msxml2.XMLHTTP");}
catch (error) {}
try {return new ActiveXObject("Microsoft.XMLHTTP");}
catch (error) {}
throw new Error("Could not create HTTP request object.");
}
You should do something like this:
var request = new XMLHttpRequest();
request.open('GET', yourURI, true); // true = async
request.send();
request.onreadystatechange(function () {
if(request.readyState === 4){
/*ENTER CODE THAT SHOULD BE EXECUTED WHEN REQUEST IS DONE
(OPTIONAL)*/
switch(request.status) {
case 200: //DO STUFF
case 404: //DO OTHER STUFF
}
}
});
Or this:
var request = new XMLHttpRequest();
request.open('GET', yourURI, true);
request.addEventListener('load', function () {
//CODE THAT SHOULD BE EXECUTED WHEN SUCCES.
});
request.addEventListener('error', function () {
//CODE THAT SHOULD BE EXECUTED WHEN ERROR RESPONE.
});
request.send();
Related
Good Easter! I am trying to return an HTTP Request from a function, but it's not working how I thought it would. Now, the HTTP request is fine, in which I can show you right here:
const Http = new XMLHttpRequest();
const url='https://api.dictionaryapi.dev/api/v2/entries/en_US/hand';
Http.open("GET", url);
Http.send();
Http.onreadystatechange = (e) => {
alert(eval(Http.responseText)[0]["meanings"][0]["partOfSpeech"])
}
As you can see, it alerts it multiple times, which is not what I want. Also, when I do it in a function:
var JSIMS = function() {
const Http = new XMLHttpRequest();
const url='https://api.dictionaryapi.dev/api/v2/entries/en_US/hand';
Http.open("GET", url);
Http.send();
Http.onreadystatechange = (e) => {
return eval(Http.responseText)[0]["meanings"][0]["partOfSpeech"];
}
}
alert(JSIMS())
As you can see there, it alerts "undefined". ( In which I purposely made the function return the value and not alert it from inside of the function )
Please so me how to fix this with the function returning the value, and not alerting it from directly inside the function.
You can avoid multiple alert by adding a condition to check if the request status is DONE.
const Http = new XMLHttpRequest();
const url='https://api.dictionaryapi.dev/api/v2/entries/en_US/hand';
Http.open("GET", url);
Http.send();
Http.onreadystatechange = (e) => {
if (Http.readyState == XMLHttpRequest.DONE) {
alert(eval(Http.responseText)[0]["meanings"][0]["partOfSpeech"])
}
}
The other option to return your response is to use a callback function to the method to handle the response from the api call as below.
function loadData(callback) {
const Http = new XMLHttpRequest();
const url='https://api.dictionaryapi.dev/api/v2/entries/en_US/hand';
Http.onreadystatechange = (e) => {
if (Http.readyState == XMLHttpRequest.DONE) {
callback(eval(Http.responseText)[0].meanings[0].partOfSpeech);
}
}
Http.open("GET", url);
Http.send();
}
loadData(myCustomFunction);
function myCustomFunction(data) {
console.log(data); /* Output - Verb */
}
Similarly we can use promise to return your data once the api responds
var JISM = () => {
return new Promise((resolve, reject)=>{
const Http = new XMLHttpRequest();
const url='https://api.dictionaryapi.dev/api/v2/entries/en_US/hand';
Http.onreadystatechange = (e) => {
if (Http.readyState == XMLHttpRequest.DONE) {
resolve(eval(Http.responseText)[0].meanings[0].partOfSpeech);
}
}
Http.onerror = () => reject(Http.statusText);
Http.open("GET", url);
Http.send();
});
}
JISM().then(data => {
console.log(data);
});
xhr.send should be used after xhr.onreadystatechange, but you aren't testing to see if(xhr.readyState === 4 && xhr.status === 200) anyways. In XHR2 you can avoid using a xhr.onreadystatechange, and using that condition, if you just do like:
function JSIMS(word){ // I don't like the name of the function... but
const xhr = new XMLHttpRequest;
xhr.open('GET', 'https://api.dictionaryapi.dev/api/v2/entries/en_US/'+word);
xhr.responseType = 'json';
return new Promise((resolve, reject)=>{
xhr.onload = function(){
resolve(this.response[0].meanings[0].partOfSpeech);
}
xhr.onerror = ()=>{
reject(new Error('load error'));
}
xhr.send();
});
}
JSIMS('hand').then(j=>{
console.log(j);
});
You should use the received data when ready state is done.
Try this and it works:
const Http = new XMLHttpRequest();
const url='https://api.dictionaryapi.dev/api/v2/entries/en_US/hand';
Http.open("GET", url);
Http.send();
Http.onreadystatechange = () => {
if (Http.readyState === XMLHttpRequest.DONE) {
alert(eval(Http.responseText)[0].meanings[0].partOfSpeech);
}
}
If I use a simple post xhr request its working to send post parameters:
var http = new XMLHttpRequest();
var url = "example url";
var params = "limit=2";
http.open("post", url);
http.onreadystatechange = function() {
if(http.readyState == 4 && http.status == 200) {
alert(http.responseText);
}
}
http.send(params);
But If I use promise with parameters (data) then I get undefined index php error, with promise I cant send parameters? or I miss something...
function postAjaxCall(url, data) {
// return a new promise.
return new Promise(function(resolve, reject) {
// do the usual XHR stuff
var req = new XMLHttpRequest();
req.open('post', url);
req.onload = function() {
if (req.status == 200) {
resolve(req.response);
}
else {
reject(Error(req.statusText));
}
};
// handle network errors
req.onerror = function() {
reject(Error("Network Error"));
};
// make the request
req.send(data);
//same thing if i hardcode like
//req.send("limit=2");
});
};
and I make the request
postAjaxCall('example url', "limit=2").then(
function(response) {
document.getElementById('example').innerHTML = response;
},
function(error) {
console.error("Failed!", error);
});
If you check your request header at your server side, you will see that your request was sent as text/plain. To make PHP see the request as a $_POST you will need to set the request header to 'application/x-www-form-urlencoded', which should come after xhr.open() and before xhr.onload. https://developer.mozilla.org/en-US/docs/Web/Guide/AJAX/Getting_Started shows a basic Ajax usage.
function postAjaxCall(url, data) {
// return a new promise.
return new Promise(function(resolve, reject) {
// do the usual XHR stuff
var req = new XMLHttpRequest();
req.open('post', url);
//NOW WE TELL THE SERVER WHAT FORMAT OF POST REQUEST WE ARE MAKING
req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
req.onload = function() {
if (req.status == 200) {
resolve(req.response);
} else {
reject(Error(req.statusText));
}
};
// handle network errors
req.onerror = function() {
reject(Error("Network Error"));
}; // make the request
req.send(data);
//same thing if i hardcode like //req.send("limit=2");
});
};
I want to give the request.onload function a variable, and add the request.response into that variable. How do I do that?
function loadathing(url, target){
var request = new XMLHttpRequest();
request.open("GET", url, true);
request.onload = function() {
target = request.response;
};
}
This doesn't work because it forgets what target is.
You have two problems here. First, in order to pass the response to target, you would need to do it on a property of target rather than target directly since target can't be a native value (native values such as strings and numbers cannot be passed by reference).
function loadathing(url, target){
var request = new XMLHttpRequest();
request.open("GET", url, true);
request.onload = function() {
target.response = request.response;
};
}
var target = {};
loadathing('foo.php', target);
Secondly, since you are performing an asynchronous ajax request, you will need to add a callback to your function so that the outer code knows when target.response is populated.
function loadathing(url, target, cb){
var request = new XMLHttpRequest();
request.open("GET", url, true);
request.onload = function() {
target.response = request.response;
cb();
};
}
var target = {};
loadathing('foo.php', target, function () {
console.log(target.response);
});
You can then simplify it by removing target all together and using the callback.
function loadathing(url, cb){
var request = new XMLHttpRequest();
request.open("GET", url, true);
request.onload = function() {
cb(request.response);
};
}
loadathing('foo.php', function (response) {
console.log(response);
});
I have a dynamic AJAX function which has many different modes (I call the function with different parameters and check via switch-case which mode has to be used) but when I call the AJAX function with different parameters directly one after another using the same XMLHttpRequest it seems that the second request overrides the first one.
Is that possible?
Here is a example for my function(EDIT):
$(function() {
ajax("test_1");
ajax("test_2");
});
function ajax(mode)
{
switch (mode)
{
case "test_1":
request = null;
if (window.XMLHttpRequest) {
request = new XMLHttpRequest();
} else if (window.ActiveXObject) {
try {
request = new ActiveXObject('Msxml2.XMLHTTP');
} catch (e) {
try {
request = new ActiveXObject('Microsoft.XMLHTTP');
} catch (e) {
}
}
}
if (!request) {
console.log("Kann keine XMLHTTP-Instanz erzeugen");
return false;
} else {
var url = "../ajax.php";
request.open('POST', url, true);
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
request.send('ajax_mode=test&request_mode=test_1');
request.onreadystatechange = function() {
if (request.readyState === 4)
{
interpretRequest_test_1();
}
};
}
break;
case "test_2":
request = null;
if (window.XMLHttpRequest) {
request = new XMLHttpRequest();
} else if (window.ActiveXObject) {
try {
request = new ActiveXObject('Msxml2.XMLHTTP');
} catch (e) {
try {
request = new ActiveXObject('Microsoft.XMLHTTP');
} catch (e) {
}
}
}
if (!request) {
console.log("Kann keine XMLHTTP-Instanz erzeugen");
return false;
} else {
var url = "../ajax.php";
request.open('POST', url, true);
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
request.send('ajax_mode=test&request_mode=test_2');
request.onreadystatechange = function() {
if (request.readyState === 4)
{
interpretRequest_test_2();
}
};
}
break;
}
}
function interpretRequest_test_1() {
if (request.status != 200) {
console.log("Error");
} else {
alert("1");
}
}
function interpretRequest_test_2() {
if (request.status != 200) {
console.log("Error");
} else {
alert("2");
}
}
Your request variable isn't defined using the var keyword, which makes it a global and thus you are reusing the same XHR object.
If you are reusing the same XMLHttpRequest object then yes, it will abort the previous request if you try to make another request with it, while the other request is in progress.
In your switch statements, define request with the var keyword. This makes the variable scoped to the local scope, and not a global.
switch (mode) {
case "test_1":
var request = null;
...
case "test_2":
var request = null;
...
}
Alternatively do it just once at the top of your function instead of in each case statement.
A more simplified example if you have a global object:
var xhr = new XMLHttpRequest();
function do_smth(){
xhr.open(...);
}
function do_smth2(){
xhr.open(...);
}
do_smth();
do_smth2();
Instead of a global, you need to make the XHR object scoped properly so each call has a unique XHR.
function do_smth(){
var xhr = new XMLHttpRequest();
xhr.open(...);
}
function do_smth2(){
var xhr = new XMLHttpRequest();
xhr.open(...);
}
I'm learning how to use sinon.js. I can fake normal AJAX but I need to request a image and I don't get xhr.response (it is undefined). How can I use sinon.js to fake the response of an image?
var url = 'http://www.google.com.hk/logos/2013/antoni_gauds_161st_birthday-1539005.2- hp.jpg';
server.respondWith("GET", url, [200, {}, '']);
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'arraybuffer';
xhr.addEventListener('load', function() {
//this.response is undefined
})
xhr.send(null);
How can I fake this image request?
I think an idea:
describe('...',function(){
var xhr;
before(function(){
xhr = sinon.useFakeXMLHttpRequest();
xhr.onCreate = function (x) {
console.log(x)
};
//....
}
after(function(){
xhr.restore();
});
it('....',function(){
xhr.prototype.response = new ArrayBuffer(1024)
// ...
});
}