I use an XMLHttpRequest inside a Promise. Because the server sometimes is idle, I would like to do 3 attemps when there is an error.
However, doing like below raise the Object state must be opened error on line xhr.send() in the function sendData(). Why?
I think the xhr is already opened. What would be the proper way to achieve this?
function _callService(url, postData) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
var attempts = 0;
xhr.open("POST", url);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
resolve(xhr.response);
}
}
};
xhr.addEventListener("error", onXhrError);
function sendData() {
//here I get the Object state must be opened when this is called from onXhrError listener
xhr.send(postData);
};
function onXhrError() {
console.log("onXhrError");
if (attempts < 3) {
attempts += 1;
sendData();
} else {
reject("OnXhrError")
}
};
sendData();
});
Schedule _callService(url, postData, attempts) to be called again instead of sendData(), see multiple, sequential fetch() Promise.
function callService(attempts) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
if (attempts < 3)
reject(++attempts)
else
resolve("done");
}, Math.floor(Math.random() * 1200))
}).catch(function(err) {
throw err
})
}
function request(n) {
return callService(n)
.then(function(data) {
console.log(data);
return data
})
.catch(function(err) {
console.log(err);
return typeof err === "number" && err < 3 ? request(err) : typeof err !== "number" ? new Error(err) : "requested " + err + " times";
})
}
request(0)
.then(function(done) {
console.log("done:", done)
})
.catch(function(err) {
console.log(err)
})
Related
I want to assign videoLoaded to true right after myVideo.mp4 is fully loaded. I can do this at the last lines of the code (This is our promise):
preload.fetch([
clipSource
]).then(items => {
// Using a promise it'll fire when we are sure that video clip has finished loading completely
videoLoaded = true;
});
The first issue is if our URL is not valid we get a 404 response status code. the 404 itself is a valid response so we will not trigger xhr.onerror() because technically it's not an error.
we can track 404 status using:
xhr.onloadend = function() {
if(xhr.status == 404) { // do something }
}
The issue is onloadend event fired only after the promise .then(items => { .... so if there is not a valid URL we can not prevent the promise to resolve and videoLoaded will be assigned to true although there is not a valid URL...
I want to resolve the promise and assign videoLoaded to true only if xhr.status !== 404 in this situation we can be sure that we have a valid URL.
Here is the code (I have used a setInterval and it works but I think there are cleaner solutions that you can share):
let onLoadPassed = false;
let videoLoaded = false;
let clipSource = 'https://mysite/myVideo.mp4';
preload();
// Make sure the video clip is fully loaded
function preload(){
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.Preload = factory());
}(this, (function () { 'use strict';
function preloadOne(url, done) {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'blob';
xhr.onprogress = event => {
if (!event.lengthComputable) return false
let item = this.getItemByUrl(event.target.responseURL);
item.completion = parseInt((event.loaded / event.total) * 100);
item.downloaded = event.loaded;
item.total = event.total;
this.updateProgressBar(item);
};
xhr.onload = event => {
let type = event.target.response.type;
let blob = new Blob([event.target.response], { type: type });
let url = URL.createObjectURL(blob);
let responseURL = event.target.responseURL;
let item = this.getItemByUrl(responseURL);
item.blobUrl = url;
item.fileName = responseURL.substring(responseURL.lastIndexOf('/') + 1);
item.type = type;
item.size = blob.size;
done(item);
};
xhr.onerror = event => {
console.log('Error has happend so we restart the video preloading..');
preload();
};
xhr.onloadend = function() {
if(xhr.status == 404){
console.log('404 not found');
onLoadPassed = false;
} else {
console.log('File exist');
onLoadPassed = true;
}
}
xhr.send();
}
function updateProgressBar(item) {
var sumCompletion = 0;
var maxCompletion = this.status.length * 100;
for (var itemStatus of this.status) {
if (itemStatus.completion) {
sumCompletion += itemStatus.completion;
}
}
var totalCompletion = parseInt((sumCompletion / maxCompletion) * 100);
if (!isNaN(totalCompletion)) {
this.onprogress({
progress: totalCompletion,
item: item
});
}
}
function getItemByUrl(rawUrl) {
for (var item of this.status) {
if (item.url == rawUrl) return item
}
}
function fetch(list) {
return new Promise((resolve, reject) => {
this.loaded = list.length;
for (let item of list) {
this.status.push({ url: item });
this.preloadOne(item, item => {
this.onfetched(item);
this.loaded--;
if (this.loaded == 0) {
this.oncomplete(this.status);
resolve(this.status);
}
});
}
})
}
function Preload() {
return {
status: [],
loaded: false,
onprogress: () => {},
oncomplete: () => {},
onfetched: () => {},
fetch,
updateProgressBar,
preloadOne,
getItemByUrl
}
}
return Preload;
})));
const preload = Preload();
preload.fetch([
clipSource
]).then(items => {
// Fired when we are sure that video clip has finished loading completely
let check = setInterval(passedFunc, 50);
function passedFunc() {
if(onLoadPassed === true){
videoLoaded = true;
clearInterval(check);
console.log('videoLoaded: ' + videoLoaded);
};
}
});
};
You can intercept the promise and throw an error if the status code is 404, this way the subsequent .then statements will be ignored and the result will be captured by the .catch statement.
preload.fetch([
clipSource
])
.then(response => {
if(!response.ok) //better to use response.ok as it checks a range of status codes
throw Error(response.statusText);
return response;
})
.then(items => {
// Using a promise it'll fire when we are sure that video clip has finished loading completely
videoLoaded = true;
})
.catch(error => {
//do something
console.log(error)
});
I have the following methods.
async getuserdevicesIDs() {
return await new Promise( (resolve, reject) => {
let timesDone = 0;
// tslint:disable-next-line: no-var-keyword
const viewDevicesLink = '/user/devices/view/'; // parameter: email
const xhr = new XMLHttpRequest();
// xhr.open('POST', this.AUTH_SERVER_ADDRESS + '/user/devices/view/', true);
xhr.open('POST', this.AUTH_SERVER_ADDRESS + viewDevicesLink, true);
xhr.setRequestHeader('Content-type', 'application/JSON;charset=UTF-8');
console.log(this.auth.getUserID());
const email = this.auth.getUserID().toString();
const us = new User();
us.name = '';
us.email = 'richard#gmail.com';
us.password = '';
xhr.send(JSON.stringify(us));
xhr.addEventListener('readystatechange', processRequest, false);
xhr.onreadystatechange = processRequest;
function processRequest(e) {
// tslint:disable-next-line: triple-equals
if (xhr.readyState == 4 && xhr.status == 200) {
// tslint:disable-next-line: triple-equals
if (timesDone == 0) {
// tslint:disable-next-line: prefer-const
const response = xhr.response;
timesDone++;
alert(response);
resolve(response);
}
// tslint:disable-next-line: triple-equals
} else if (xhr.readyState == 4) {
alert('server error: ' + xhr.status + ', response is: ' + xhr.responseText);
timesDone++;
return null;
}
}
});
}
This method ask the server for certain values and The server will return an arrya of values but I do not know how to resolve the response as an arry for later use in this method.
this.getuserdevicesIDs().then(response => {
alert(this.IDs); })
.catch(err => {});
If anyone knows how i can store the response i get from server into an array that would help me out a great deal. Thank you
When receiving data from a web server, the data is always a string.
Parse the data with JSON.parse(), and the data becomes a JavaScript object.
resolve(JSON.parse(response));
I have the following method.
async getuserdevicesIDs() {
let timesDone = 0;
// tslint:disable-next-line: no-var-keyword
const viewDevicesLink = '/user/devices/view/'; // parameter: email
const xhr = new XMLHttpRequest();
// xhr.open('POST', this.AUTH_SERVER_ADDRESS + '/user/devices/view/', true);
xhr.open('POST', this.AUTH_SERVER_ADDRESS + viewDevicesLink, true);
xhr.setRequestHeader('Content-type', 'application/JSON;charset=UTF-8');
console.log(this.auth.getUserID());
const email = this.auth.getUserID().toString();
const us = new User();
us.name = '';
us.email = 'richard#gmail.com';
us.password = '';
xhr.send(JSON.stringify(us));
xhr.addEventListener('readystatechange', processRequest, false);
xhr.onreadystatechange = processRequest;
function processRequest(e) {
// tslint:disable-next-line: triple-equals
if (xhr.readyState == 4 && xhr.status == 200) {
// tslint:disable-next-line: triple-equals
if (timesDone == 0) {
// tslint:disable-next-line: prefer-const
const response = xhr.response;
timesDone++;
return response;
}
// tslint:disable-next-line: triple-equals
} else if (xhr.readyState == 4) {
alert('server error: ' + xhr.status + ', response is: ' + xhr.responseText);
timesDone++;
return null;
}
}
}
that is working fine but when i call the method like this
var IDs = await this.getuserdevicesIDs();
alert(IDs[0]);
then the alert fires before the getuserdevicesIDs() method has completed even if I await it. Any idee on how i can force the alert to wait for the method to finish? Thanks for any help
Try returning a Promise inside getuserdevicesIDs() function like this
async getuserdevicesIDs() {
return await new Promise((resolve, reject) => {
//your code here ...
resolve(value); // when you want to return a value in promise
}
}
When you want to call the method
this.getuserdevicesIDs().then(response => {}).catch(err => {});
How to use promises (ES6) and .then method in order to this code will work?
getGif: function (searchingText, callback) {
var url = GIPHY_API_URL + '/v1/gifs/random?api_key=' + GIPHY_PUB_KEY + '&tag=' + searchingText;
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onload = function () {
if (xhr.status === 200) {
var data = JSON.parse(xhr.responseText).data;
var gif = {
url: data.fixed_width_downsampled_url,
sourceUrl: data.url
};
callback(gif);
}
};
xhr.send();
},
Using Promise-Based XHR your code looks like:
getGif = function (searchingText) {
return new Promise((resolve, reject)=>{
var url = GIPHY_API_URL + '/v1/gifs/random?api_key=' + GIPHY_PUB_KEY + '&tag=' + searchingText;
var xhr = new XMLHttpRequest();
// Setup our listener to process compeleted requests
xhr.onreadystatechange = function () {
// Only run if the request is complete
if (xhr.readyState !== 4) return;
// Process the response
if (xhr.status >= 200 && xhr.status < 300) {
// If successful
var data = JSON.parse(xhr.responseText).data;
var gif = {
url: data.fixed_width_downsampled_url,
sourceUrl: data.url
};
resolve(gif);
} else {
// If failed
reject({
status: request.status,
statusText: request.statusText
});
}
};
xhr.open('GET', url);
xhr.send();
});
}
Need to invoke method depends on signature of function.
getGif(searchText).then((response)=>{
console.log(response);
}, (error)=> {
console.log(error);
})
I'm trying to make a Javascript function that gets two arguments, an URL and a data, posts it to a PHP and returns the server's response without jQuery or any library. (Source)
function post(URL, data) {
return new Promise(function(resolve, reject) {
var req = new XMLHttpRequest();
req.open('POST', URL, true);
req.setRequestHeader('Content-type',
'application/x-www-form-urlencoded');
req.onload = function() {
if (req.status == 200) {
resolve(req.response);
} else {
reject(Error(req.statusText));
}
};
req.onerror = function() {
reject(Error('Network Error'));
};
req.send(data);
});
}
<?php
if ( isset($_POST['data']) ) {
echo json_encode(array("Hey {$_POST['data']}" ));
} else {
echo json_encode(array("Error"));
}
?>
So far so good, here's how I'm handling it.
function handle(URL, data) {
post(URL, data).then(function(response) {
return response;
}, function(error) {
return error;
});
}
However, this always returns undefined. Interestingly, if I try to console.log(response) it works fine.
I want to be able to do such things like alert(handle(URL, data));
Is this impossible? If yes how could I get around it?
I ended up reworking my script to use callback instead.
var AJAX = {
get: function(a, b) {
var c = new XMLHttpRequest();
c.onreadystatechange = function() {
if (c.readyState == 4 && c.status == 200) {
b(c.responseText);
}
};
c.open('GET', a, true);
c.send();
},
post: function(a, b, d) {
var c = new XMLHttpRequest();
c.onreadystatechange = function() {
if (c.readyState == 4 && c.status == 200) {
d(c.responseText);
}
};
c.open('POST', a, true);
c.setRequestHeader('Content-type',
'application/x-www-form-urlencoded');
c.send(b);
}
};
This is how you call it: AJAX.get('text.txt', function doSomething(what){console.log(what)});