JavaScript Wait until all async calls finish - javascript

I need some help with handling async calls in JavaScript. I have a for loop, each loop calls an async HttpRequest, and adds its response to an array. I want the program to wait until all the async calls are finished before proceeding without jQuery (which is only used for DOM manipulation). I've searched quite bit for solutions but none really worked without heavily changing my code or relying on jQuery.
function httpGet(theUrl, callback) {
var xmlRequest = new XMLHttpRequest();
xmlRequest.onreadystatechange = function() {
if (xmlRequest.readyState == 4 && xmlRequest.status == 200) {
callback(xmlRequest.responseText);
}
}
xmlRequest.open("GET", theUrl, true);
xmlRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xmlRequest.setRequestHeader("Accept", "application/json");
xmlRequest.send(null);
}
$(document).ready(function() {
var channels = ["freecodecamp", "storbeck", "terakilobyte", "habathcx","RobotCaleb","thomasballinger","noobs2ninjas","beohoff"];
var urls = channels.map((x) => "https://api.twitch.tv/kraken/channels/" + x);
var data = [];
(function(urls, data) {
urls.forEach(function(url) {
function(resolve, reject) {
httpGet(url, function(response) {
data.push(JSON.parse(response));
})
};
})
})(urls, data);
// Continue after all async calls are finished
})
UPDATED: Edited with Promise, but still not working, maybe I did something wrong.
function httpGet(theUrl, callback) {
return new Promise(function(resolve, reject) {
var xmlRequest = new XMLHttpRequest();
xmlRequest.onreadystatechange = function() {
if (xmlRequest.readyState == 4 && xmlRequest.status == 200) {
callback(xmlRequest.responseText);
}
}
xmlRequest.open("GET", theUrl, true);
xmlRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xmlRequest.setRequestHeader("Accept", "application/json");
xmlRequest.send(null);
})
}
$(document).ready(function() {
var channels = ["freecodecamp", "storbeck", "terakilobyte", "habathcx","RobotCaleb","thomasballinger","noobs2ninjas","beohoff"];
var urls = channels.map((x) => "https://api.twitch.tv/kraken/channels/" + x);
var data = [];
var promises = [];
(function(urls, data) {
urls.forEach(function(url) {
var promise = httpGet(url, function(response) {
data.push(JSON.parse(response));
});
promises.push(promise);
})
Promise.all(promises).then(function() {
console.log(data);
})
})(urls, data);
})

With promises, you should not use a callback parameter. Call the resolve/reject functions from the promise instead.
Instead of passing a callback to the call, chain the things you want to do with the result of the promise in a .then handler.
function httpGet(theUrl) {
return new Promise(function(resolve, reject) {
var xmlRequest = new XMLHttpRequest();
xmlRequest.onreadystatechange = function() {
if (xmlRequest.readyState == 4) {
if (xmlRequest.status == 200)
resolve(xmlRequest.responseText);
// ^^^^^^^
else
reject(new Error(xmlRequest.statusText)); // or something
}
}
xmlRequest.open("GET", theUrl, true);
xmlRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xmlRequest.setRequestHeader("Accept", "application/json");
xmlRequest.send(null);
});
}
$(document).ready(function() {
var channels = ["freecodecamp", "storbeck", "terakilobyte", "habathcx", "RobotCaleb", "thomasballinger", "noobs2ninjas", "beohoff"];
var urls = channels.map((x) => "https://api.twitch.tv/kraken/channels/" + x);
var promises = urls.map(function(url) {
// ^^^ simpler than forEach+push
var promise = httpGet(url); // <-- no callback
return promise.then(JSON.parse);
});
Promise.all(promises).then(function(data) {
// ^^^^
console.log(data);
});
})

Can't it be done by just keeping the count of ajax requests as a variable:
var urls_count, data_count = 0;
function httpGet(theUrl, callback, onComplete) {
var xmlRequest = new XMLHttpRequest();
xmlRequest.onreadystatechange = function() {
if (xmlRequest.readyState == 4 && xmlRequest.status == 200) {
callback(xmlRequest.responseText);
}
if(xmlRequest.readyState == 4){
data_count += 1
if(urls_count == data_count){
//this is called when all ajax calls complete
onComplete();
}
}
}
xmlRequest.open("GET", theUrl, true);
xmlRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xmlRequest.setRequestHeader("Accept", "application/json");
xmlRequest.send(null);
}
$(document).ready(function() {
var channels = ["freecodecamp", "storbeck", "terakilobyte", "habathcx","RobotCaleb","thomasballinger","noobs2ninjas","beohoff"];
var urls = channels.map((x) => "https://api.twitch.tv/kraken/channels/" + x);
var data = [];
urls_count = urls.length;
var onComplete = function(){
//your code after all ajax completes.
}
(function(urls, data) {
urls.forEach(function(url) {
function(resolve, reject) {
httpGet(url, function(response) {
data.push(JSON.parse(response));
}, onComplete)
};
})
})(urls, data);
})

Since you are using jQuery you can use the Deferred Object to chain promises.
Collect all the promises and use $.when with spread operator to wait for all promises to resolve. You can use then to run a function after all ajax requests are resolved.
ES5 Example
$(document).ready(function () {
var channels = ["freecodecamp", "storbeck", "terakilobyte", "habathcx", "RobotCaleb", "thomasballinger", "noobs2ninjas", "beohoff"];
var urls = channels.map(function (x) {
return "https://api.twitch.tv/kraken/channels/" + x;
});
var data = [];
var promises = urls.map(function (url) {
return $.get(url).then(function (response) {
data.push(response);
});
});
$.when.apply($, promises).then(function () {
console.log('done', data);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
ES6 Example
$(document).ready(function() {
var channels = ["freecodecamp", "storbeck", "terakilobyte", "habathcx","RobotCaleb","thomasballinger","noobs2ninjas","beohoff"];
var urls = channels.map((x) => "https://api.twitch.tv/kraken/channels/" + x);
var data = [];
var promises = urls.map((url) => $.get(url).then((response) => {
data.push(response);
}));
$.when(...promises).then(function() {
console.log('done', data);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Related

console.log doesn't wait "await" in async function

I have this code
async function dataget(APIURL){
let x = false;
let xhr = new XMLHttpRequest();
xhr.open("GET", APIURL);
xhr.send();
xhr.onload = await function(){
if(1 == 1){
x = true
}else{
x = "gg"
}
}
console.log(x)
}
console.log(dataget("data.json"));
I want console to wait until onload function ends (when x = true), but this doesn't happen,
console returns false and doesn't wait
this is the output:
I want an explanation not just the solve
You need to turn the dataget function to return a promise which will resolve after the onload function executed, so you can await it and return the result.
function dataget(APIURL){
return new Promise((resolve, reject) => {
let x = false;
let xhr = new XMLHttpRequest();
xhr.open("GET", APIURL);
xhr.send();
xhr.onload = function(){
if(1 == 1){
resolve(true)
}else{
resolve("gg")
}
}
})
}
(async () => {
const result = await dataget("data.json")
console.log(result); // true
})()
Here's one way to make the code do what you want
async function dataget(APIURL) {
const x = await new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open("GET", APIURL);
xhr.send();
xhr.onload = () => {
if (1 == 1) {
resolve(true);
} else {
resolve("gg");
}
};
});
console.log(x);
return x;
}
(async () => {
console.log(await dataget("data.json"));
})();

Is it possible to use AJAX Delete request without jquery

I wanted to use AJAX delete request without using jquery is it possible to do that my json object at localhost:8000 looks like this :
{
"Students":[{"Name":"Kaushal","Roll_No":30,"Percentage":94.5},
{"Name":"Rohit","Roll_No":31,"Percentage":93.5},
{"Name":"Kumar","Roll_No":32,"Percentage":45.5}]}
I want to have a delete button which can delete a single record.Code would be appreciated.
function loadDoc(){
var table2="<tr><th>Name</th><th>Roll_No</th><th>Percentage</th></tr>"
var url2="http://localhost:8000/Students"
var xhttp2=new XMLHttpRequest();
xhttp2.onreadystatechange = function(){
if(xhttp2.readyState === 4 && xhttp2.status === 200){
var jsonArr = JSON.parse(xhttp2.responseText);
for(i in jsonArr){
table2 += "<tr><td>"+jsonArr[i].Name +
"</td><td> "+jsonArr[i].Roll_No+
"</td><td>"+jsonArr[i].Percentage+"</td><tr>"
}
document.getElementById("mytable").innerHTML = table2;
}
}
xhttp2.open('GET',url2,true);
xhttp2.send();
table,th,td{
border:1px solid black;
border-collapse: collapse;
}
<button onclick="loadDoc()" >Get Data</button>
<br>
<br>
<table id="mytable">
</table>
This should work:
function request() {
this.http = new XMLHttpRequest();
}
request.prototype.delete = function(url, callback) {
this.http.open("DELETE", url, true);
var self = this;
this.http.onload = function() {
if (self.http.status === 200) {
callback(null, "Record Deleted");
} else {
callback("Error: " + self.http.status);
}
};
this.http.send();
};
Another example (maybe clearer):
deleteRecord('https://jsonplaceholder.typicode.com/posts/1');
function deleteRecord(url) {
var xhr = new XMLHttpRequest();
xhr.open("DELETE", url);
xhr.send();
xhr.onload = function() {
if (xhr.status != 200) {
console.log('ERROR');
} else {
console.log('DELETED!');
}
};
xhr.onerror = function() {
console.log('NO CONNECTION');
};
}
You can use JS Fetch API to get this done,
const url = 'https://www.example.com/route';
const deleteFetch = async (url) => (
const response = await fetch(url, {
method: ‘DELETE’
});
const myJson = await response.json(); // converting to json
return myJson ;
}

Ionic wait for method to complete before continuing

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 => {});

AJAX with promise

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);
})

How to retrieve the data being returned from the promise?

I am using browser-perf to get the results of browser performance.
i have the following code:
var browserPerf = require('browser-perf');
var _ = require('lodash');
var fs = require('fs');
var colors = require('colors');
var couchbase = require('couchbase');
const Promise = require('bluebird');
var performanceMetricsDriver = {
recordPerfMetrics: function(url) {
var self = this;
var perf, loadTime, domInteractive, firstPaint;
var perfData = {};
fs.readFile('urls.txt', 'UTF-8', function (err,urls) {
if (err) {
return console.log(err);
}
var urls = urls.split("\n");
urls.shift();
urls.forEach(function(url) {
console.log(url);
self.getStats(url);
});
// console.log(colors.magenta("Starting to record performance metrics for " + url));
// this.storePerfMetrics();
});
},
getData: function(url) {
return new Promise(function (resolve, reject) {
console.log("NOW GETTING DATA FOR URL: " + url);
// if (err) {
// Promise.reject("This erred out!!");
// } else {
// Promise.resolve(data);
// console.log(data);
// loadTime = (data.loadEventEnd - data.navigationStart)/1000 + ' sec';
// firstPaint = data.firstPaint;
// domInteractive = (data.domInteractive - data.navigationStart)/1000 + ' sec';
// perfData = {
// 'URL' : url,
// 'firstPaint' : firstPaint,
// 'loadTime' : loadTime,
// 'domInteractive' : domInteractive
// };
// }
}).then(function(data, err) {
});
},
getStats: function(url) {
var self = this;
browserPerf(url, self.getData(url), {
selenium: 'http://localhost:4444/wd/hub',
browsers: ['chrome']
});
}
}
I am trying to capture the data being returned by browser-perf, but for some reason this keeps on erring out and gives me an unhandled rejection error
You can only use the data returned by a promise inside the .then() handler. I don't know exactly what you're trying to do, but here's one idea:
getStats: function(url) {
this.getData(url).then(function(data) {
browserPerf(url, data, {
selenium: 'http://localhost:4444/wd/hub',
browsers: ['chrome']
});
});
}
This assumes that .getData() returns a promise that will eventually get resolved with your data (something your code does not show).

Categories