I am using Async XMLHttpRequest to make an API call. Here's the workflow of my program,
first_function(){
var valueToBeReturned = 0;
makeRequest(myCallback)//function for API call
/*rest of the code*/
console.log("print second");
return valueToBeReturned;
}
function makeRequest(callback){
var xhttp = new XMLHttpRequest();
xhttp.open("GET", "my_url", true);
xhttp.send(null);
xhttp.onload = function() {
if(xhttp.readyState === 4) {
if(xhttp.status === 200) {
response = JSON.parse(xhttp.responseText);
callback(null, response);
}
}
}
}
function myCallback(data){
console.log("print first")
}
Now what happens is every time I run it, the entire code in the first function is executed and then the code in makeRequest is executed. I understand JS is synchronous in nature and everything. But I'm not able to get my work done here, which is fisrt it makes API call, then callback is executed, then the code after makeRequest. What am I doing wrong here?
PS this is not the actual code, just to demonstrate the flow of my program
You need to put callback as a parameter in makeRequest. I'm not sure what that null is there for, though. If you want "print second" to print second, you'll need to execute it after myCallback - maybe insert another callback?
function first_function(){
var valueToBeReturned = 0;
makeRequest(myCallback, restOfTheCode)
function restOfTheCode() {
/*rest of the code*/
console.log("print second");
}
}
function makeRequest(callback1, callback2){
var xhttp = new XMLHttpRequest();
xhttp.open("GET", "my_url", true);
xhttp.send(null);
xhttp.onload = function() {
if(xhttp.readyState === 4 && xhttp.status === 200) {
const response = JSON.parse(xhttp.responseText);
callback1(response);
callback2(response);
}
}
}
function myCallback(data){
console.log("print first");
}
But this whole thing would be a whole lot nicer if you used Fetch and Promises instead:
function makeRequest() {
return fetch('my_url')
.then(response => response.JSON())
}
// consume the request:
makeRequest()
.then(responseJSON => {
// do stuff with the responseJSON
});
Related
I have implemented an async function which creates an XHR object inside a Promise, requests a data from the server and fulfills the Promise with the response received from the server
async function playWordsAudio(id, type, check=false){
let file_namePromise = new Promise(function (onSuccess, onFailure){
var xhttp = new XMLHttpRequest();
xhttp.onload = function(){
if(this.readyState == 4 && this.status == 200){
if(this.responseText !== "No Data"){
file_name = this.responseText;
if(check == false){
audio = new Audio('assets/uploads/wav/' + file_name);
audio.play();
}else{
onSuccess(file_name);
}
}else{
if(check){
onSuccess('Not Found');
}
}
}
};
xhttp.open("GET", "scripts/get_word_audio.php?id=" + id + "&type=" + type, true);
xhttp.send();
});
let resultant = await file_namePromise;
if(check){
return resultant;
}
}
Later I have defined another function which calls the async function shown above
function modify_object(id, type, obj){
result = playWordsAudio(id, type, true);
result.then(function(value){
if(value == "Not Found"){
obj.classList.remove('fa-play');
}
});
}
Later in the implementation I also call the modify_object function and pass it an object for modification as described in the function.
modify_object(id, type, plays[i]);
In the async function I have created a Promise, on success I pass it the file_name received from the XHR response or else I pass it 'Not Found'.
Here I am confused on how the await part works inside the asyc function.
It returns the resultant to the caller of the async function which is
result = playWordsAudio(id, type, true);
If so, what does the onSuccess method in the Promise do
let file_namePromise = new Promise(function (onSuccess, onFailure){
/*
Some Codes
*/
onSuccess(file_name);
/*
Some more code
*/
onSuccess('Not Found);
/*
Rest of the Code
*/
}
As I have checked by commenting out the return statement after the await statement
if(check){
// return resultant;
}
The Promise is not fulfilled.
How does each part of the implementation work as described above?
I want to run an Ajax request after another Ajax request has fired. If there are multiple Ajax request(Requests A and B, and I want to fire request C if A is fired.) in the same page, how can I target the request I want? What do I need to run to fill up the code below?
xhr.addEventListener('readystatechange', function(event) {
if (xhr.readyState === 4 && xhr.status === 200 && Request A fired) {
Make Request C;
}
});
I am looking for a solution without jQuery.
You could make your request into a function that accepts a callback function. This function is called whenever the request is completed and will continue your code with the data that is received.
function request(url, callback) {
const xhr = new XMLHTTPRequest();
xhr.addEventListener('readystatechange', function(event) {
if (xhr.readyState === 4 && xhr.status === 200 && typeof callback === 'function') {
callback(xhr.responseText);
}
});
xhr.open('GET', url, true);
xhr.send();
}
It would work like this with nested callbacks and can be shuffled in any order that you would like.
request('request-a', function(dataA) {
// Request A has finished here.
// Now start request B.
// dataA is the xhr.responseText value.
request('request-b', function(dataB) {
// Request B has finished here.
// Now start request C.
request('request-c', function(dataC) {
// Request C has finished here.
});
});
});
This is the simplest way to make your request reusable and to act whenever your request has been finished. A more modern approach would be to use the Fetch API, which is a promise based interface that does the same thing as XMLHTTPRequest. Be sure to check it out.
You are looking for a system to make xhr requests synchronously. Try nested promises, where next request is made when previous promise resolves.
let promiseA = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, 'foo');
});
promiseA.then((x) => {
// Launch request B
}).then((x) => {
// Launch request C
}).then((x) => {
// Launch request D....
});
There's a good explanation on that
Use fetch, promises and async await to make your life easier.
async function handlerA() {
log('Starting request A...')
let responseA = await fetch(urls.a)
let a = await responseA.text()
log('Starting request C...')
let responseC = await fetch(urls.c)
let c = await responseC.text()
log(a + c)
}
async function handlerB() {
log('Starting request B...')
let responseB = await fetch(urls.b)
let b = await responseB.text()
log(b)
}
const urls = {
a: 'https://httpbin.org/json',
b: 'https://httpbin.org/robots.txt',
c: 'https://httpbin.org/xml',
}
function log(str) {
document.getElementById('output').innerText += `${str}\n`
}
document.getElementById('btnA').addEventListener('click', handlerA)
document.getElementById('btnB').addEventListener('click', handlerB)
<button id="btnA">Button A</button>
<button id="btnB">Button B</button>
<div id="output"></div>
I found a solution that serves my need. Simply creates another XMLRequest object and use them in the if statement.
let requestA = new XMLHttpRequest();
let requestB = new XMLHttpRequest();
if(requestA.readyState === 4){
//Run request C
}
Here is the code I'm working with (IP address censored for obvious reasons):
async function buildJobsView() {
let jobList = await getJobs()
Promise.all([getJobs()]).then($("#jobsPane").text(jobList))
}
async function getJobs() {
//Open API connection and submit
var url = "http://IPADDRESS:8082/api/jobs?IdOnly=true"
var xhr = new XMLHttpRequest()
xhr.open("GET", url, true)
xhr.send()
xhr.onreadystatechange = function() {
if(xhr.readyState == 4 && xhr.status == "200") {
return xhr.response
}
}
}
For whatever reason, the jobList variable is being assigned before the getJobs() function finishes running. The getJobs() function does return the right output eventually, but the code has already moved on. What am I doing wrong?
async doesn't automatically convert callback-based code into Promise-based code - you have to explicitly convert the callback to a Promise and return a Promise whenever you want to be able to use it as a Promise.
function getJobs() {
return new Promise((resolve) => {
//Open API connection and submit
var url = "http://IPADDRESS:8082/api/jobs?IdOnly=true"
var xhr = new XMLHttpRequest()
xhr.open("GET", url, true)
xhr.send()
xhr.onreadystatechange = function() {
if(xhr.readyState == 4 && xhr.status == "200") {
resolve(xhr.response)
}
}
});
}
Then, getJobs will return a Promise, and then you can consume it with await:
const jobList = await getJobs()
I'm writing webpage with a javascript to read data files in text format from the server per user request. Once the text file has been loaded, I need to manipulate the data somewhat.
I have been using XMLHttpRequest for the loading, however, now I see that synchronous requests are "deprecated". I can't start manipulating the data before it's loaded, so what can I do in this case?
Use an asynchronous request (or fetch, see below, which is also asynchronous):
function doGET(path, callback) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
// The request is done; did it work?
if (xhr.status == 200) {
// ***Yes, use `xhr.responseText` here***
callback(xhr.responseText);
} else {
// ***No, tell the callback the call failed***
callback(null);
}
}
};
xhr.open("GET", path);
xhr.send();
}
function handleFileData(fileData) {
if (!fileData) {
// Show error
return;
}
// Use the file data
}
// Do the request
doGET("/path/to/file", handleFileData);
Or using promises, which are the more modern way to handle callbacks (but keep reading):
function doGET(path, callback) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
// The request is done; did it work?
if (xhr.status == 200) {
// Yes, use `xhr.responseText` to resolve the promise
resolve(xhr.responseText);
} else {
// No, reject the promise
reject(xhr);
}
}
};
xhr.open("GET", path);
xhr.send();
});
}
// Do the request
doGET("/path/to/file")
.then(function(fileData) {
// Use the file data
})
.catch(function(xhr) {
// The call failed, look at `xhr` for details
});
Here in 2019, there's no reason to use XHR wrapped in a promise like that, just use fetch:
function doGET(url) {
return fetch(url).then(response => {
if (!response.ok) {
throw new Error("HTTP error " + response.status); // Rejects the promise
}
});
}
Since you want to handle the local file, Try this
Make use of XMLHttpRequest
function readFile(file)
{
var f = new XMLHttpRequest();
f.open("GET", file, false);
f.onreadystatechange = function ()
{
if(f.readyState === 4)
{
if(f.status === 200 || f.status == 0)
{
var res= f.responseText;
alert(res);
}
}
}
f.send(null);
}
Then you have to call with File:\\
readFile('File:\\\yourpath');
I'm new to JavaScript programming. I'm now working on my Google Chrome Extension. This is the code that doesn't work... :P
I want getURLInfo function to return its JSON object, and want to put it into resp. Could someone please fix my code to get it work?
function getURLInfo(url)
{
var xhr = new XMLHttpRequest();
xhr.open
(
"GET",
"http://RESTfulAPI/info.json?url="
+ escape(url),
true
);
xhr.send();
xhr.onreadystatechange = function()
{
if (xhr.readyState == 4)
{
return JSON.parse(xhr.responseText);
}
}
}
var resp = getURLInfo("http://example.com/") // resp always returns undefined...
Thanks in advance.
You are dealing with an asynchronous function call here. Results are handled when they arrive, not when the function finishes running.
That's what callback functions are for. They are invoked when a result is available.
function get(url, callback) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
// defensive check
if (typeof callback === "function") {
// apply() sets the meaning of "this" in the callback
callback.apply(xhr);
}
}
};
xhr.send();
}
// ----------------------------------------------------------------------------
var param = "http://example.com/"; /* do NOT use escape() */
var finalUrl = "http://RESTfulAPI/info.json?url=" + encodeURIComponent(param);
// get() completes immediately...
get(finalUrl,
// ...however, this callback is invoked AFTER the response arrives
function () {
// "this" is the XHR object here!
var resp = JSON.parse(this.responseText);
// now do something with resp
alert(resp);
}
);
Notes:
escape() has been deprecated since forever. Don not use it, it does not work correctly. Use encodeURIComponent().
You could make the send() call synchronous, by setting the async parameter of open() to false. This would result in your UI freezing while the request runs, and you don't want that.
There are many libraries that have been designed to make Ajax requests easy and versatile. I suggest using one of them.
You can't do it at all for asynchronous XHR calls. You cannot make JavaScript "wait" for the HTTP response from the server; all you can do is hand the runtime system a function to call (your handler), and it will call it. However, that call will come a long time after the code that set up the XHR has finished.
All is not lost, however, as that handler function can do anything. Whatever it is that you wanted to do with a return value you can do inside the handler (or from other functions called from inside the handler).
Thus in your example, you'd change things like this:
xhr.onreadystatechange = function()
{
if (xhr.readyState == 4)
{
var resp = JSON.parse(xhr.responseText);
//
// ... whatever you need to do with "resp" ...
//
}
}
}
For small edit talking about post: https://stackoverflow.com/a/5362513/4766489
...
if (typeof callback == "function") {
//var resp = xhr.responseText;
var resp = JSON.parse(xhr.responseText);
callback(resp);
}
...
And when you call
...
function(data) {
alert(data);
/* now do something with resp */
}
...