For some reason my async call is not working as expected. Here's what I'm trying to do:
Make several ajax calls in a loop
On success, push some API data to a global array
Use that array in another function (e.g print it out)
var authcodes = ["E06000001","E06000002","E06000003"];
var dict = [];
async function ajaxCall() {
for(var i=0;i<authcodes.length;i++){
$.ajax({
type: "GET",
url: 'https://api.coronavirus.data.gov.uk/v1/data?filters=areaCode=' + authcodes[i] +'&structure={"areaCode":"areaCode","cumCasesByPublishDate":"cumCasesByPublishDate"}',
dataType: "json",
success: function(data) {
dict.push(data.data[0].cumCasesByPublishDate);
}
});
} return dict;
}
async function printArr () {
const someApiRes = await ajaxCall()
console.log(someApiRes[1]); //this doesn't work
console.log(dict[1]); //this doesn't work
}
printArr();
Here is the JSFiddle with code commented: https://jsfiddle.net/gjv9hrpo/1/
I understand that the printArr() function has to be ran after the array is populated due to the async nature which I hoped the await would solve. Am I using it wrong?
Thanks.
Use promise.All()
async function ajaxCall() {
var promises = [];
for(var i=0;i<authcodes.length;i++){
promises.push($.ajax({
type: "GET",
url: 'https://api.coronavirus.data.gov.uk/v1/data?filters=areaCode=' + authcodes[i] +'&structure={"areaCode":"areaCode","cumCasesByPublishDate":"cumCasesByPublishDate"}',
dataType: "json",
success: function(data) {
dict.push(data.data[0].cumCasesByPublishDate);
}
}));
}
await Promise.all(promises);
return dict;
}
It might make more sense to do this with promises.
Promise.all lets you know when either all input promises have fulfilled or when one of them rejects.
var authcodes = ["E06000001", "E06000002", "E06000003"];
function ajaxCalls() {
return Promise.all(
authcodes.map((code) => {
return new Promise(async (resolve) => {
const response = await fetch(
`https://api.coronavirus.data.gov.uk/v1/data?filters=areaCode=${code}&structure={"areaCode":"areaCode","cumCasesByPublishDate":"cumCasesByPublishDate"}`
)
const json = await response.json();
resolve(json.data[0].cumCasesByPublishDate);
});
})
);
}
function printArr() {
ajaxCalls().then((values) => {
console.log(values);
}).catch(e => {
console.error(e)
});
}
printArr();
Promise.allSettled gives you a signal when all the input promises are settled, which means they’re either fulfilled or rejected. This is useful in cases where you don’t care about the state of the promise, you just want to know when the work is done, regardless of whether it was successful.
var authcodes = ["E06000001", "E06000002", "E06000003"];
function ajaxCalls() {
return Promise.allSettled(
authcodes.map((code, i) => {
return new Promise(async (resolve, reject) => {
if (i ===0 ) reject('something went wrong')
const response = await fetch(
`https://api.coronavirus.data.gov.uk/v1/data?filters=areaCode=${code}&structure={"areaCode":"areaCode","cumCasesByPublishDate":"cumCasesByPublishDate"}`
);
const json = await response.json();
resolve(json.data[0].cumCasesByPublishDate);
});
})
);
}
async function printArr() {
const results = await ajaxCalls();
console.log(results.map((result) => result.value || result.reason));
}
printArr();
Related
I tried to make an https call and return the body containing the data, but when I tried to print it in the console it returned undefined so I used the "promises".
const https = require('https');
const baseUri = "https://apiv2.gofile.io/"
class apiGofile {
constructor(email,apikey) {
this.email=email;
this.apikey=apikey;
}
getBestServer() {
return new Promise((resolve,reject)=>{
https.get(baseUri + "getServer", (res) => {
let body="";
res.on("data", (data) => {
body += data;
});
res.on("end", () => {
body = JSON.parse(body);
resolve(body.data.server);
});
res.on("error",(e)=>{
reject(e);
});
});
});
};
}
let a = new apiGofile("a","b");
a.getBestServer()
.then(
response => console.log(response),
error => console.log(error)
);
is there any way to use await and async in my code?
You can move the following code into an asynchronous main function then call that:
async function main() {
const a = new apiGoFile("a", "b");
// await the promise instead of using .then
const response = await a.getBestServer();
console.log(response);
// you can now use response here
}
// make sure to call the function
main();
You can read more about async/await here.
You can also make class methods async:
class Foo {
async myMethod() {
const a = new apiGoFile("a", "b");
const response = await a.getBestServer();
console.log(response);
}
}
need so help with some JavaScript code that isn't working as expected.
The Application sends files names to the server via Ajax calls to be processed but I want to tell the database that current import has been completed when they are all done. The issue I'm having is the code to update the database with the state runs before any of the ajax calls run.
I have ready in a number of area on the web that you could potentially create a promise array to track that but JavaScript honestly being a weak point of mine, I'm not sure how to I would go about implementing it.
below are my current code snippets:
function that loops through file names:
function importFiles() {
serverImporterInit();
let files = getFiles();
if (files) {
showImportProgressModal(files);
let looper = $.Deferred().resolve();
$.when.apply($, $.map(files, function (file, index) {
let fileStatusElement = document.getElementById('file-' + index + '-status');
let fileErrorElement = document.getElementById('file-' + index + '-error');
looper = looper.then(function () {
setTimeout(function () {
return import_file_request(file, fileStatusElement, fileErrorElement);
}, 2000);
});
return looper;
})).then(function () {
});
}
}
ajax call to the server:
function import_file_request(file, element, errorElement) {
let deferred = $.Deferred();
fileImportStatusInProgress(element);
$.ajax({
type: 'POST',
url: '/importer/manual_import',
data: {'file': file.toString()},
success: function(data) {
fileImportStatusSuccess(element);
deferred.resolve(data);
},
error: function (error) {
fileImportStatusFailed(error, element, errorElement);
deferred.reject(error);
}
});
return deferred.promise();
}
Both of these functions have been derived from other tutorials across the web but I'm not entirely sure if they ever did what I originally intended as I have only just got around to trying to track the completion status due to another requirement.
Any help would be great. Also if there any other details I can include to make this question a little better for other please let me know and I will update accordingly.
Update
I have tried to update the code to use a promise array but still having no luck.
File loop:
const importFiles = async (files) => {
serverImporterInit()
const filesLength = files.length
showImportProgressModal(files);
for (let i = 0; i < filesLength; i++) {
const requests = files.map((file) => {
let fileStatusElement = document.getElementById('file-' + i + '-status');
let fileErrorElement = document.getElementById('file-' + i + '-error');
return import_file_request(file, fileStatusElement, fileErrorElement) // Async function to import file
.then(console.log(file + " successfully imported"))
.catch(e => console.log('Error'))
})
await Promise.all(requests)
.then(serverImporterClose())
.catch(e => console.log(''))
}
}
File import request to sever:
function import_file_request(file, element, errorElement) {
fileImportStatusInProgress(element);
return new Promise((resolve, reject) => {
$.ajax({
type: 'POST',
url: '/importer/manual_import',
data: {'file': file.toString()},
success: function(data) {
fileImportStatusSuccess(element);
resolve();
},
error: function (error) {
fileImportStatusFailed(error, element, errorElement);
reject();
}
});
})
}
You seem to be using jQuery so I try to give a jQuery based solution without the native promise. I think you should either use the native promise combined with the builtin fetch() function and no jQuery or jQuery alone.
The key is to use $.map() to give back an array of promises and then wait for all of them using $.when(). Also it is always important to return the jQuery promise.
function importFiles() {
var files = getFiles();
if (files) {
showImportProgressModal(files);
serverImporterInit();
var promises = $.map(files, function(file, index) {
let fileStatusElement = document.getElementById('file-' + index + '-status');
let fileErrorElement = document.getElementById('file-' + index + '-error');
return import_file_request(file, fileStatusElement, fileErrorElement)
})
$.when.apply($, promises).then(function(results) {
serverImporterClose();
results.forEach(function(result) {
if (result) {
console.log("yay, success");
}
else {
console.log("failed");
}
})
hideImportProgressModal(files);
});
}
}
function import_file_request(file, element, errorElement) {
fileImportStatusInProgress(element);
// MUST return the promise here
return $.ajax({
type: 'POST',
url: '/importer/manual_import',
data: {'file': file.toString()},
success: function(data) {
fileImportStatusSuccess(element);
return true;
},
error: function (error) {
fileImportStatusFailed(error, element, errorElement);
return false;
}
});
}
Is there any difference in the way these two chains are handled or are they handled in the same way? Is there any benefit to using one over the other?
I've tried both calls and they both return the same result (32) -- and my assumption is that they process in the same manner but I have a friend who is telling me they work differently.
const getNewDataPromise = num => new Promise( (resolve, reject) => {
typeof num === 'number' ? resolve(num * 2) :
reject(`${num} is not a number -- input must be a numeric value.`);
});
getNewDataPromise(2).then( data => {
const nowEight = getNewDataPromise(data);
return nowEight;
}).then( data => {
const nowSixteen = getNewDataPromise(data);
return nowSixteen;
}).then( data => {
const nowThirtyTwo = getNewDataPromise(data);
return nowThirtyTwo
}).then( data => {
console.log(data);
}).catch( err => {
console.log(err);
});
getNewDataPromise(2).then( data => {
return getNewDataPromise(data);
}).then( data => {
return getNewDataPromise(data);
}).then( data => {
return getNewDataPromise(data);
}).then( data => {
console.log(data);
}).catch( err => {
console.log(err);
});
There is no difference at all between your two versions in terms of outcome. The first one just creates an intermediate local variable which doesn't affect the outcome or parallelism or anything like your friend has asserted.
Your second one is more concise and just as clear and would be my preference between your two.
Another option would be to use async/await which is particularly useful for sequences of asynchronous operations:
async function run() {
try {
let data = await getNewDataPromise(2);
data = await getNewDataPromise(data);
data = await getNewDataPromise(data);
data = await getNewDataPromise(data);
console.log(data);
} catch(e) {
console.log(e);
}
}
Or, if you were really just calling the same function over and over, you could use a loop too which would be a bit less repetitive (more DRY):
async function run() {
try {
let data = 2;
for (let i = 0; i < 4; i++) {
data = await getNewDataPromise(data);
}
console.log(data);
} catch(e) {
console.log(e);
}
}
I use fetch to get data for each element of an array. Those values are pushed into an array and I want to return the max of those values once all values are fetched.
For that I used a promise:
async function init() {
await maxWaste(nut0Codes).then(response => {
console.log(response);
});
}
function maxWaste(nutCodes) {
return new Promise(resolve => {
let waste = [];
nutCodes.forEach((element) => {
let query = queryMaxWaste(element);
fetch(address + encodeURIComponent(query))
.then(response => {
return response.json();
})
.then(response => {
waste.push(response.results.bindings[0].wasteGeneration.value);
console.log(waste);
});
});
let maxWaste = Math.max(waste);
resolve(maxWaste);
});
}
I am not sure where my mistake is but the resolve happens before the fetch is done :
I receive the zero from the console.log(response) and I don't know why it is not working.
Any advice would be appreciated!
If you're going to write code that uses async, you should actually leverage async. If this needs to be run synchronously-ish, you can await within a for loop. If you want them all to run simultaneously, use a map and Promise.all.
async function init() {
const response = await maxWaste(nut0Codes);
console.log(response);
}
async function maxWaste(nutCode) {
const waste = [];
for (const element in nutCode) {
const query = queryMaxWaste(element);
const response = await fetch(address + encodeURIComponent(query));
const json = await response.json();
waste.push(json.results.bindings[0].wasteGeneration.value);
console.log(waste);
}
const maxWaste = Math.max(waste);
return maxWaste;
}
You could also try writing it like this so that you don't wait for each response to complete before running the next fetch:
async function init() {
const response = await maxWaste(nut0Codes);
console.log(response);
}
async function maxWaste(nutCode) {
const waste = await Promise.all(nutCode.map(async element => {
const query = queryMaxWaste(element);
const response = await fetch(address + encodeURIComponent(query));
const json = await response.json();
return json.results.bindings[0].wasteGeneration.value;
}));
return Math.max(waste);
}
Lets say I am calling a dispatch method in my actions :
return dispatch(add_data(data)).then(function(response){console.log("Done")}
and my add_data method looks like:
export function add_data(data){
return (dispatch, getState) => {
for(var i in data){
var data = {'url': data[i]}
return dispatch(myApi({url: "some_url", method: "POST", data: data,}
)).then(response => {
console.log(response)
return response.json()
}).then(response => {
console.log(response)
return dispatch({response, type: types.ADD_DATA})
})
}
}
}
It is working for only first data. Only first data is dispatched. It is inside loop and I expect it to dispatch for all the data from loop.
How can I achieve this ?
Thank you
As soon as you return, The return statement exits the function. If you don’t want to return from a function in the middle of the loop, don’t return from it in the middle of the loop.
You can use Promise.all(promises) to create a promise that resolves when all the child promises have resolved. You can collect promises into an array as you create them, and later return Promise.all(promises) to represent them all:
export function add_data(data){
return (dispatch, getState) => {
var promises = [];
for (var key in data) {
var itemData = {'url': data[key]}
var promise = myApi({
url: "some_url",
method: "POST",
data: itemData
}).then(
response => response.json()
).then(response => {
dispatch({response, type: types.ADD_DATA})
})
promises.push(promise)
}
return Promise.all(promises)
}
}