How to use async await inside another async function - node.js

i have a question about using async await inside another promise. I have a function call another function to get a transaction details.
When i running the function LastTransactions the field details do not show results. Anyone can help me ?
LastTransactions: async (transactionKey, page) => {
const api = `https://api.pagar.me/1/payables?recipient_id=${transactionKey}&count=${totalResults}&page=${page}&api_key=${PagarmeApiKey}`;
const response = await axios.get(api);
transactions = response.data.map((item) => {
return {
id : item.id,
transactionId : item.transaction_id,
trxDetails : [transactionDetails(item.transaction_id)],
}
});
return transactions;
},
and a detail function
async function transactionDetails(id){
const response = await axios.get(`https://api.pagar.me/1/transactions/${id}?api_key=${PagarmeApiKey}`)
const data = response.data;
return data;
}

You need to utilize the Promise.all method to take an array of promises and return an array with your transactions once each individual call for transaction details finishes.
async (transactionKey, page) => {
const api =
`https://api.pagar.me/1/payables?recipient_id=${transactionKey}&count=${totalResults}&page=${page}&api_key=${PagarmeApiKey}`;
const response = await axios.get(api);
// create an array of promises and wait for
// all of them to resolve before continuing
const transactions = await Promise.all(
response.data.map(async item => {
const { id, transaction_id } = item;
// get transaction details for each item in the array
const trxDetails = await transactionDetails(transaction_id);
return {
id,
trxDetails,
transactionId: transaction_id,
};
})
);
return transactions;
};
References:
Promise.all() - MDN

Since transactionDetails(item.transaction_id) is Asynchronous, you need to await that as well, otherwise it will return immediately and trxDetails will contain a promise object, and not response.data.
try this:
transactions = response.data.map(async (item) => {
return {
id : item.id,
transactionId : item.transaction_id,
trxDetails : [await transactionDetails(item.transaction_id)],
}
});

Related

Synchronously iterate through firestore collection

I have a firebase callable function that does some batch processing on documents in a collection.
The steps are
Copy document to a separate collection, archive it
Run http request to third party service based on data in document
If 2 was successful, delete document
I'm having trouble with forcing the code to run synchronously. I can't figure out the correct await syntax.
async function archiveOrders (myCollection: string) {
//get documents in array for iterating
const currentOrders = [];
console.log('getting current orders');
await db.collection(myCollection).get().then(querySnapshot => {
querySnapshot.forEach(doc => {
currentOrders.push(doc.data());
});
});
console.log(currentOrders);
//copy Orders
currentOrders.forEach (async (doc) => {
if (something about doc data is true ) {
let id = "";
id = doc.id.toString();
await db.collection(myCollection).doc(id).set(doc);
console.log('this was copied: ' + id, doc);
}
});
}
To solve the problem I made a separate function call which returns a promise that I can await for.
I also leveraged the QuerySnapshot which returns an array of all the documents in this QuerySnapshot. See here for usage.
// from inside cloud function
// using firebase node.js admin sdk
const current_orders = await db.collection("currentOrders").get();
for (let index = 0; index < current_orders.docs.length; index++) {
const order = current_orders.docs[index];
await archive(order);
}
async function archive(doc) {
let docData = await doc.data();
if (conditional logic....) {
try {
// await make third party api request
await db.collection("currentOrders").doc(id).delete();
}
catch (err) {
console.log(err)
}
} //end if
} //end archive
Now i'm not familiar with firebase so you will have to tell me if there is something wrong with how i access the data.
You can use await Promise.all() to wait for all promises to resolve before you continue the execution of the function, Promise.all() will fire all requests simultaneously and will not wait for one to finish before firing the next one.
Also although the syntax of async/await looks synchronous, things still happen asynchronously
async function archiveOrders(myCollection: string) {
console.log('getting current orders')
const querySnapshot = await db.collection(myCollection).get()
const currentOrders = querySnapshot.docs.map(doc => doc.data())
console.log(currentOrders)
await Promise.all(currentOrders.map((doc) => {
if (something something) {
return db.collection(myCollection).doc(doc.id.toString()).set(doc)
}
}))
console.log('copied orders')
}

Awaiting coordinates from geocoding google maps API returns pending promise

I am trying to return coordinates of given adress with gooogle maps geocoding API and fetch. I am able to log these coordinates inside my get fuction, but I have no idea how to return them from the function to use it somewhere else in the code. Already tried multiple varaitions of two approaches:
function getCoordinates1(name) {
locObj = fetch(`https://maps.googleapis.com/maps/api/geocode/json?address=${name}&key=mykey`).then( (res) => res.json()).then( (data) =>
{
console.log(data.results[0].geometry.location);
return data.results[0].geometry.location;
}).then((res) => res);
}
let coordinates1 = getCoordinates1(latinaze(name2));
console.log(coordinates1);
async function getCoordinates2(name) {
locObj = await fetch(`https://maps.googleapis.com/maps/api/geocode/json?address=${name}&key=mykeyk`).then( (res) => res.json()).then( (data) =>
{
console.log(data.results[0].geometry.location);
//return data.results[0].geometry.location;
}).then((res) => res);
return locObj
}
let coordinates2 = await getCoordinates2(latinaze(name2));
console.log(coordinates2);
First function returns undefined, second returns pending promise. What am I doing wrong?
The first function returns undefined because you don't return anything. As simple as that ;)
The second function returns a pending promise because you don't wait for the promise to be resolved. The promise gets resolved when the callback inside then is invoked, but that happens after you return locObj in getCoordinates2.
You should try this:
// function definition
async function getCoordinates3(name) {
const resp = await fetch(`https://maps.googleapis.com/maps/api/geocode/json?address=${name}&key=mykeyk`)
const data = await resp.json();
return data.results[0].geometry.location;
}
// usage
const coordinates3 = await getCoordinates3(latinaze(name3));
I couldn't return any value from function, so I made it a class method and set a property in function body. Now I can get the value form that property after I call the function:
export default class SearchModel {
constructor() {
this.start = '';
this.meta = '';
this.coors = [];
this.address = 'none';
}
//translate coordinates to address
async getAdress(coordinates) {
try {
let geocodeCoordinates = `https://maps.googleapis.com/maps/api/geocode/json?latlng=${this.coors[0]},${this.coors[1]}&key=${process.env.API_GM_KEY}`
const rawData = await fetch(geocodeCoordinates);
//console.log(await rawData.json());
return await rawData.json();
} catch (error) {
return new Error(`Wild ERROR occured, can't get LocObj. Details: ${error}`);
} }
async displayAdress(coordinates) {
const data = await this.getAdress(coordinates);
const dataAdress = await data.results[0].formatted_address;
this.address = await dataAdress; }
}

Regarding nested Promises in NodeJS

There are many threads on this and using that i am trying to find solution for below problem.
getMethod() {
execDBQuery(sqlQuery1)
.then(productionQueryRows => {
prodResultFunc(resultSet) // Calling a function to get another set of DB values
.then (result) {
console.log(result) // Final result which will be sent to user
}
});
}
async function prodResultFunc(prodRowSet) {
const results = await Promise.all(
prodRowSet.map( async (objBatchRow) => {
execDBQuery(sqlQuery2)
.then(resultSet => {
read value-1
})
execDBQuery(sqlQuery3)
.then(resultSet => {
read value-2
})
// Create a object using Value-1 & Value-2 and return object to map
});
);
}
I tried implementing below (with one just SQL execution) and it works fine.
async function prodResultFunc(prodRowSet) {
const results = await Promise.all(
prodRowSet.map( async (objBatchRow) => {
execDBQuery(sqlQuery2)
.then(resultSet => {
read values using resultSet
Create object with resultSet values
})
return object
});
);
}
But i want use values for both SQLs (2 & 3) for creating object and this where i am struggling to find syntax for implementation. Any help/pointers to existing threads will be a great help.
Whenever you're dealing with complex Promise chains, I find that the async/await syntax is much easier to read. You end up with a lot less nested code.
The example below shows how you might combine the results of two different queries:
const prodRowSet = [1,2,3];
// Mock query function.
async function execDBQuery(sql) {
return [{ id: 1, query: sql }];
}
async function prodResultFunc(prodRowSet) {
const results = await Promise.all(
prodRowSet.map( async (objBatchRow) => {
let resultSet2 = await execDBQuery('sqlQuery2');
let resultSet3 = await execDBQuery('sqlQuery3');
// Do whatever you want to combine the result sets.
let combinedObj = { resultSet2, resultSet3}
return combinedObj;
})
);
return results;
}
(async() => {
let output = await prodResultFunc(prodRowSet);
console.log("Output:", output);
})();
You might be looking for Promise.All
async function prodResultFunc(prodRowSet) {
const results = await Promise.all(
prodRowSet.map( async (objBatchRow) => {
Promise.all([execDBQuery(sqlQuery2), execDBQuery(sqlQuery3)]).then(function(values) {
//TODO:: Impement you business logic here
console.log(values);
});
});
);
}

NodeJS execute the next process after then() [duplicate]

This question already has answers here:
Using async/await with a forEach loop
(33 answers)
Closed 3 years ago.
I'm trying to do upsert inside forEach becase the request is an array of objects using Sequelize.upsert method. I do this:
async createInfo (req, res){
newsData = req.body.news,
newsList = [];
newsData.forEach(async values => {
var news = {};
news.inserted_id = values.inserted_id;
if(news.inserted_id == null){
do {
news.inserted_id = crypto.getRandom(5);
var check = await InstitutionNews.count({where: {inserted_id: news.inserted_id}});
} while (check > 0);
}
News.upsert({
institution_id: institution_id,
inserted_id: news.inserted_id,
news_date: values.news_date,
news_title: values.news_title,
description: values.news_description,
created_by: created_by
}).then(ResNews => {
news.news_date = values.news_date;
news.news_title = values.news_title;
news.description = values.news_description;
newsList.push(news);
})
})
console.log("TEST")
}
but the process stop at the then(). It didn't execute the next code like the console.log.
Is there any way to execute next line code after the then(). I need then() because I wanna push the news object into newsList array. Because I need newsList as the if else conditional to do the next process.
Thanks.
Since it sounds like you need to wait for forEach to complete before you do another step, so I'd suggest using something like Promise.all:
async function createInfo(req, res) {
const newsData = req.body.news
const newsList = []
try {
await Promise.all(
newsData.map(
async (values) => {
const news = {}
news.inserted_id = values.inserted_id
// ...
News.upsert({...})
.then(ResNews => {
// ...
newsList.push(news)
})
}
)
)
console.log('newsList', newList)
// do other work
} catch (error) {
// handle errors appropriately
}
}
This way, you're creating an array of promises and waiting for all of them to resolve/finish.
Your current approach with forEach won't work in this case since it won't wait for each asynchronous call to finish before executing the "next" step. Since Promise.all returns a single Promise that you can then "wait" for to resolve before continuing with your next step.
Here's a simple example that somewhat does what you're trying to do:
async function createInfo() {
const newsData = [1, 2, 3, 4, 5]
const newsList = []
await Promise.all(
newsData.map(
async (values) => {
const temp = await Promise.resolve('temp')
console.log('first async call inside values of', values)
Promise.resolve('resolved')
.then((result) => {
newsList.push(`resolved with ${values}`)
})
}
)
)
console.log('newsList after')
console.log(newsList)
}
createInfo()
EDIT
Here's an alternative solution as rightly pointed by #Bergi in the comments:
async function createInfo(req, res) {
const newsData = req.body.news
try {
const newList = await Promise.all(
newsData.map(
async (values) => {
const news = {}
news.inserted_id = values.inserted_id
// ...
// since it doesn't look like you're using any
// data that you'd get back from the `News.upsert` call, wait for
// it to finish and just simply return your `news` object
await News.upsert({...})
news.news_date = values.news_date
news.news_title = values.news_title
news.description = values.news_description
return news
}
)
)
console.log('newsList', newList)
// do other work
} catch (error) {
// handle errors appropriately
}
}
use await instead of then like
await News.upsert({
institution_id: institution_id,
inserted_id: news.inserted_id,
news_date: values.news_date,
news_title: values.news_title,
description: values.news_description,
created_by: created_by
});
news.news_date = values.news_date;
news.news_title = values.news_title;
news.description = values.news_description;
newsList.push(news);

Async/Await and Then not working in my case

I have a function which contains a thousand of objects in an array:
function Alltransaction(transactionArray) {
transactionArray.map(async (transaction) => {
dataAgainsthash = await web3.eth.getTransaction(transaction)
TransactionObject = {
transactionHash : transaction,
from : dataAgainsthash.from
};
transactionArray.push(TransactionObject)
console.log("transaction array", transactionArray)
});
}
then i have another function which stores these thousands of object array into db
function saveTransactionToDb() {
console.log("after loop",transactionArray)
transactionss = new Transaction({
blockNumber : blockNumbers ,
transactions : transactionArray
})
// Now save the transaction to database
await transactionss.save();
// console.log("save to database")
}
then I call this in my router
await Alltransaction(transactionArray);
await saveTransactionToDb();
and I also try
Alltransaction(transactionArray).then(saveTransactionToDb())
But it always runs saveTransactionToDb() before the array of object populates the Alltransaction() method
have you try the async keyword before saveTransactionToDb and Alltransaction functions??
async function Alltransaction(transactionArray){
// your code
}
async function saveTransactionToDb(){
// your code logic*
await transactionss.save();
}
First, in Alltransaction the promise must be returned as well. In your code the function starts some processes but doesn't not await on it. Also, do not push the promises to the original array. I'm not sure what you were trying to accomplish there. Because mapping over the array gives you an array of promises, you can unify all of them with Promise.all().
function Alltransaction(transactionArray) {
const promises = transactionArray.map(async (transaction) => {
dataAgainsthash = await web3.eth.getTransaction(transaction)
const TransactionObject = {
transactionHash : transaction,
from : dataAgainsthash.from
};
return TransactionObject;
});
return Promise.all(promises);
}
Change saveTransactionToDb to receive an array instead of using the original array.
Then you'll be able to call it as:
const t = await Alltransaction(transactionArray);
await saveTransactionToDb(t);
Your second try it's not correct:
Alltransaction(transactionArray).then(saveTransactionToDb())
It's the same as:
const t = Alltransaction(transactionArray);
const s = saveTransactionToDb();
t.then(s)
That's why saveTransactionToDb doesn't way for transactions to complete. To use then, just pass the function without calling it:
Alltransaction(transactionArray).then(saveTransactionToDb)

Resources