This question already has answers here:
Using async/await with a forEach loop
(33 answers)
Closed 6 years ago.
I have the following function that works as expected:
createObjectFrom(record) {
let obj = {};
this.opts.transformers.forEach((transformer, index) => {
const headerIndex = findIndex(this.headers, (header) => {
return header === transformer.column;
});
const value = transformer.formatter(record[headerIndex]);
obj[transformer.field] = value;
});
return obj;
}
I want to refactor it to use async await and call an async function in the body of the forEach like this:
createObjectFrom(record) {
let obj = {};
this.opts.transformers.forEach(async (transformer, index) => {
const headerIndex = findIndex(this.headers, (header) => {
return header === transformer.column;
});
const result = await this.knex('managers').select('name')
console.log(result);
const value = transformer.formatter(record[headerIndex]);
obj[transformer.field] = value;
});
return obj;
}
This will obviously break the function as the forEach is now executing asynchronously and the function will just execute and leave.
Is there a way I can use async await for the forEach to execute in a synchronous manner. Could I refactor to generators?
You can not enforce a normal JS function to wait on async behavior. There is no way!
So you would have to refactor your createObjectFrom to be async as well. And then probably go for map/reduce instead of forEach.
To be performant you don't want to do this:
for(transformer of this.opts.transformers) {
await this.knex('managers').select('name');
}
Instead you should use await Promise.all(...).
However in your case the call to knex seems not to depend on the transformer, so you can do this:
async createObjectFrom(record) {
let obj = {};
const result = await this.knex('managers').select('name')
this.opts.transformers.forEach(async (transformer, index) => {
const headerIndex = findIndex(this.headers, (header) => {
return header === transformer.column;
});
console.log(result);
const value = transformer.formatter(record[headerIndex]);
obj[transformer.field] = value;
});
return obj;
}
However if you want to do something like fetch for each item something async do it like this:
async foo(data) {
const subDataArr = await Promise.all(data.map(record => loadSubData(record)));
return subDataArr;
}
Related
I have a problem... thant's a code:
class Currency {
cosnstructor() {
this.currencyInfo = [];
}
getCurrency(getInfo) {
this.currencyInfo = getInfo;
}
}
const actuallyCurrency = new Currency;
(async () => {
const response = await fetch(`http://api.nbp.pl/api/exchangerates/tables/A`);
const data = await response.json();
const currency = data[0].rates;
currency.map(element => curArr.push(element));
})();
const curArr = [];
actuallyCurrency.getCurrency(curArr);
this code working good, but I need in this.currencyInfo a new array, not reference to array curArr.
I this this is what you want:
class Currency {
constructor() {
this.currencyInfo = [];
}
getCurrency(getInfo) {
this.currencyInfo = [...getInfo]; // <-- change this line
}
}
const actuallyCurrency = new Currency;
(async () => {
const response = { json: () => { return [{rates:{a:1, b:2, c:3}}];}};
// const response = await fetch(`http://api.nbp.pl/api/exchangerates/tables/A`);
const data = await response.json();
const currency = data[0].rates;
for(key in currency) curArr.push(currency[key]);
actuallyCurrency.getCurrency(curArr);
console.log(actuallyCurrency.currencyInfo);
})();
const curArr = [];
Some thing for you to understand:
1-... is an operator that does a shallow copy of it's argument. So using as above you'll get a new array in currencyInfo.
2-Why actuallyCurrency.getCurrency(curArr); console.log(actuallyCurrency.currencyInfo); have to be inside the function ?
because os the async nature of the operation. Asyncs are postponed to when the execution has finished so the execution arrives in actuallyCurrency.getCurrency(curArr) BEFORE curArr is populated. This makes the internal currencyInfo array being null and not being populated again after execution.
3-Why this currency.map(element => curArr.push(element)); doesn't work ?
Because currency is an object, not an iterable array. If you want to iterate the elements of an object you have to options: get it's keys as an array, iterate this array and then get the value using it's key OR using for...in as I did.
Hope this is enough. Fell free to ask any question you'd like
There are a few improvements to be made. Probably the most important is arranging to check the currencyInfo instance variable after the fetch completes. This and other suggestions indicated by comments...
class Currency {
cosnstructor() {
this.currencyInfo = [];
}
// methods that assign (and don't return anything) ought to be called "set" something
setCurrency(array) {
this.currencyInfo = array;
}
// it probably makes sense to have this class do it's own async initialization
async fetchCurrency() {
const url = `http://api.nbp.pl/api/exchangerates/tables/A`;
// try/catch, so we can respond to failures
try {
const response = await fetch(url);
const data = await response.json();
// no need to map and not sure why the array needs to be copied. I suspect
// it doesn't but [...array] copies array
this.setCurrency([...data[0].rates]);
} catch (error) {
console.log('error fetching', error);
}
}
}
// instantiation requires ()
const actuallyCurrency = new Currency();
// no async/await at the top level
actuallyCurrency.fetchCurrency().then(() => {
console.log(actuallyCurrency.currencyInfo);
})
I Have 2 functions one which uses async and await to grab data and place it into an array.
The second is acts like a checker to see if the user inputs a similar value as seen on the database
function repeatsChecker() {
let api_data_values = []
fetchData().then(data => {
for (let i = 0; i < data.length; i++) {
api_data_values.push(data[i].name)
}
})
return api_data_values
}
// testing for similarities
async function test() {
let name = "Joe"
let test = await repeatsChecker();
console.log(test[0])
}
test()
When I compile a simple if statement everything returns true and when I do console.log(test[0])
it returns undefined?
repeatChecker isn't returning a promise, so the fact that you're awaiting doesn't have any meaningful effect. console.log(test[0]) executes before api_data_values.push(data[i].name).
Try this:
function repeatsChecker() {
return fetchData().then(data => {
return data.map(value => value.name);
});
}
Or with async/await:
async function repeatsChecker() {
const data = await fetchData();
return data.map(value => value.name);
}
This question already has answers here:
How to return many Promises and wait for them all before doing other stuff
(6 answers)
Closed 2 years ago.
I am new to React JS. In my application I am facing a situation where I need to call an API multiple times with different url, like apiurl.com/abc, apiurl.com/xyz. These abc and xyz are stored in an array. Hence I wanted to use .map() to change the url to make multiple api calls. But in .map() async await wont work, so looking for some solutions if any. I have gone through few possible solution like with promises, but could not implement.
Here is my code:
export const someAction = () => async (dispatch, param) => {
let myArray = ["abc", "xyz"];
let id= "";
param1 = "someauthcode";
myArray.map((x) => {
id = x;
const myResponse = await loaders.myResponseApi(param1, id); *//This does not work as await should be in async call*
});
dispatch({ type: types.ARRAY_API, payload: myResponse });
}
So the idea is to make 2 api calls with apiurl.com/abc, apiurl.com/xyz. I have constructed the url (apiurl.com) in an different file.
Thanks in advance.
Turn your array into an array of promises and then use Promise.all
export const someAction = () => async(dispatch) => {
try {
const payload = await Promise.all(myArray.map(id => loaders.myResponseApi(param1,id)));
dispatch({type:types.ARRAY_API,payload});
} catch(err) {
// an error occurred in at least one of the promises
}
}
Instead of using .map() you can use a traditional for or while loop:
export const someAction = () => async (dispatch, param) => {
let myArray = ["abc", "xyz"];
let id= "";
param1 = "someauthcode";
let i = 0;
while (i < myArray.length) {
id = myArray[i];
const myResponse = await loaders.myResponseApi(param1, id);
// handle response here...
i++;
}
}
I'm new in async function. I'm trying to apply it for UrlFetchApp.fetch.When there is a network problem, it throws an undefined error because there is no response code available. Therefore I gave a try to async function.
var addStamp = obj => {
obj.date = new Date();
return obj;
}
var decideWho = (form) => {
var choice = form.choice;
var obj = { key1 : 'func1(form.input1)',
key2 : 'func2(form.input2)'......};
return addStamp(eval(obj[choice]||obj[default]));
}
const fetchUrl = async (url, params) => {
let data = await UrlFetchApp.fetch(url,params).getContentText();
return JSON.parse(data);
}
var func1 = async (val) => {
var params = {...};
let response = await fetchUrl(val, params);
return response["message"];
}
var func2 = async (val) => {
var params = {...};
let response = await UrlFetch.app(val, params);
if (response.getResponseCode() === 200) {
var result = {step:"file", result:response};
} else var result = {step: null, result: ""};
return result;
}
when func1 is called through decideWho, it returns result and addStamp is performed so date is added to the return value.
But when func2 is called, it returns value correctly but from decideWho, addStamp is not performed before it return so 'date' is undefined. In this incident, I have to call addStamp within the func2 while returning the value.
How to avoid this undefined?
I'm sure I'm messing up with async/await.
I tried to put async/await in decideWho by declaring extra value with await then wrap it with addStamp then return it.
let result = await eval(obj[choice]||obj[default]);
return addStamp(result);
}
However when decideWho is called from clientside with google.script.run, it throws undefined error. I guess somewhere I need to add await one more time but I can't find out where.
Couple of points worth to mention
(*Because the form from client side calls various function, I decided to put bunch into one bucket. i.e. regardless of the option, it will call only decideWho of server function from the client, and through option's value, it will choose which function will be called with parameters. And each function returns value and addStamp from decideWho so that I don't have to repeat this process/code in each and every function.
**Only func1 and func2 are async but not others.)
This worked
const addStamp = async (obj) => {
const temp = await obj;
temp.date = new Date();
return temp;
}
Or shorter version
const addStamp = async (obj) => (
{ ...(await obj),
date: new Date()})
Thanks to Josh and Andy
I think this should work for you. It has several other refactors in it too...
The basic issue looks to me that you are not awaiting your async functions when you add them to the object.
// use const - variables are unreliable!
// Rest Spread syntax
const addStamp = obj => ({
...obj,
date: new Date();
})
// this can throw on a network or JSON parse error,
// the fn that calls (and awaits / .then chains)
// on decideWho needs to handle that.
const decideWho = async form => {
const { choice } = form;
// you need to await these, they are async
const obj = {
key1 : await func1(form.input1),
key2 : await func2(form.input2)
};
return addStamp(eval(obj[choice]||obj[default]));
}
// can throw from network or JSON parse
const fetchUrl = async (url, params) => {
let data = await UrlFetchApp.fetch(url,params).getContentText();
return JSON.parse(data);
}
const func1 = async val => {
const params = {...};
let response = await fetchUrl(val, params);
return response["message"];
}
const func2 = async val => {
const params = {...};
const response = await UrlFetch.app(val, params);
// use a ternary, and no intermediate var
return (response.getResponseCode() === 200) ?
{step:"file", result:response} :
{step: null, result: ""};
}
}
I am currently working with destructuring arrays in Javascript, I would like to access these variables in other functions but currently, I am struggling to figure out how I might go about this.
I've tried calling the function and then console.log(thermostatArray) -> I believe this returned pending promise
I've tried calling the function and awaiting it and then console.log thermostatArray.
dataFormat() is properly able to see log and use the array but heatCallCheck() is not and I am not seeing past the issue yet.
var express = require("express");
var router = express.Router();
const multer = require("multer");
var Excel = require("exceljs");
const index = require("../routes/index");
const workbook = new Excel.Workbook();
async function convertFile(workbook) {
csvWorkbook = workbook.csv.readFile("./uploads/uploadedFile.csv");
await csvWorkbook.then(async function(csvWorkbook) {
const worksheet = workbook.getWorksheet("sheet1");
try {
// await dataFormat(worksheet);
await heatCallCheck(worksheet,)
} catch (err) {
console.log(err);
}
await workbook.xlsx.writeFile("./uploads/convertedFile.xlsx").then(() => {
console.log("converted file written");
});
});
}
async function dataFormat(worksheet) {
let thermostatArray = []
await csvWorkbook.then(async function(worksheet) {
const serialNum = worksheet.getCell("D1").value;
const thermostatName = worksheet.getCell("D2").value;
const startDate = worksheet.getCell("D3").value;
const endDate = worksheet.getCell("D4").value;
const thermostat = worksheet.eachRow({includeEmpty: true}, function(row,rowNumber){
if (rowNumber > 6) {
thermostatArray.push(row.values)
}
})
console.log(`${thermostatArray[5]} Array Sample from dataFormat` )
console.log(`${thermostatArray[6]} Array Sample from dataFormat` )
return thermostatArray
})}
async function heatCallCheck(worksheet,thermostatArray) {
let test = await dataFormat(worksheet).then(thermostatArray => {
return thermostatArray[5]
}).catch(err => {
console.error(err)
})
console.log(`${test} result `)
}
My expected results, in this case, would be that I would be able to see the 4th element in thermostat array using the heatCallCheck() function.
I figured I would be able to access it after the .then is called.
my understanding is that .then(thermostatArray =>
makes that array the return value.
You do this:
async function someFunction() {
const myResultFromAPromise = await functionThatReturnsAPromise();
// ....do some stuff
return myResultFromAPromise;
}
OR
function someFunction() {
return functionThatReturnsAPromise().then(function(myResultFromAPromise) {
// ...do some stuff
return myResultFromAPromise;
});
}
but don't do both, it's just terribly confusing.
EDIT: as a commenter pointed out, you can await anything, but it's clear from your code that you're very confused about the point of async/await