Add await inside .then - javascript

I have below code in which i want to call an await after getting response in .then.
const info = new getInfo(this.fetchDetails);
info
.retrieve()
.then((res) => {
const details = this.getLatestInfo(res, 'John');
})
.catch((error) => {
console.log(error);
});
In nutshell want to make const details = this.getAgentInfo(res, 'John'); as const details = await this.getLatestInfo(res, 'John'); but its giving error as "await is only valid in async function", how to make this async?

Mixing then() and its syntactic variant async / await is like speaking with two different accents. Rewriting the code to strictly use each variation, we get:
Older way:
function promiseReturningFunction() {
const info = new getInfo(this.fetchDetails);
return info.retrieve().then((res) => {
return this.getLatestInfo(res, 'John');
}).catch((error) => {
console.log(error);
});
}
// call it
promiseReturningFunction().then(res => {
// res will be the result of getLatestInfo()
}
Newer way:
async function promiseReturningFunction() {
const info = new getInfo(this.fetchDetails);
try {
const res = await info.retrieve();
const info = await this.getLatestInfo(res, 'John');
return info;
} catch (error) {
console.error(error);
}
}
// call it
const res = await promiseReturningFunction(); // res will be the result of getLatestInfo()

You need to use async keyword to make the function async as shown below.
const info = new getInfo(this.fetchDetails);
info
.retrieve()
.then(async (res) => {
const details = await this.getLatestInfo(res, 'John');
})
.catch((error) => {
console.log(error);
});

Related

Javascript async function return then-catch promise?

Introduction
I am new in the world of javascript promises and I need to understand how they work correctly...
Until know I have been doing this in my code:
const handleRefresh = () => {
setIsRefreshing(true);
fetchUserData()
.then(async () => { <--------- Using then because I return a promise in fetchUserData
await fetchUserPosts(); <------- Using await here
setIsRefreshing(false);
}).catch(err => { <--------- This will catch the error I have thrown in the function fetchUserPosts or inside the then body
// TODO - Show error
setIsRefreshing(false);
console.log(err)
);
};
const fetchUserData = async () => { <------ async function
const { firebase } = props;
const userId = firebase.getCurrentUser().uid;
const documentRef = firebase.getDatabase().collection("users").doc(userId);
// Fetch all the user information
return documentRef <--------- Returning the promise here
.get()
.then((doc) => {
if (doc.exists) {
// Get all user data
const data = doc.data();
console.log(data);
setUserData(data);
}
})
.catch((err) => {
throw err; <----------- Throwing error
});
};
I don't know if I am doing anti patterns... But basically I need to know if this is a good way and if I am doing this correctly.
Questions
Do I have to declare the fetchUserData function as async to return a promise?
Can I use the async await in the then/catch body?
Can I do this?
const handleRefresh = async () => {
setIsRefreshing(true);
await fetchUserData()
.then(async () => { <--------- Using then because I return a promise in fetchUserData
await fetchUserPosts(); <------- Using await here
}).catch(err => { <--------- This will catch the error I have thrown in the function fetchUserPosts or inside the then body
// TODO - Show error
console.log(err)
);
setIsRefreshing(false);
};
I would really appreciate if someone guides me. Thanks.
the words async and await are only syntactic sugar for then and catch.
This:
fetchUserData()
.then(data => return data )
.catch(error => return error)
is equivalent to:
async function getUserData() {
const userData = await fetchUserData()
return userData
}
Here you are returning anything (success or error). If you want to treat the error here, just put a try/catch clause.
async function getUserData() {
try {
return await fetchUserData()
} catch (e) {
return e.message
}
}
Note that you can only use the await clause within an async function.
1.
Function can return Promise without being declared as async as long as you don't await inside it,
2.
You should not use async-await inside then, simply return a Promise and it'll be resolved in the following then,
3.
When using async-await syntax, Promises are awaited in a declarative way as demonstrated below:
const handleRefresh = async () => {
try
{
const a = await getA()
// pass the result to another Promise
const b = await getB(a)
const c = await getC(b)
} catch (error)
{
handleError(error)
}
};

console.log not logging with await variable

I am trying to log the data of a promise to my console but it's not showing. i have tried defining then in then and on top of functions and tried with let and redefining the before executing the algorithm but. no response
sample
var trade;
const getTrades = async () => {
return await axios({
method: 'get',
url: bUrl + tradeQuery
})
}
const getSOrders = async () => {
return await axios({
method: 'get',
url: bUrl + mOrderQuery
})
}
const postOrder = async() => {
const binanceRest = new api.BinanceRest({
...
}
)
binanceRest.newOrder({
...
})
.then(async(data) => {
const trades = await getTrades()
const mOrders = await getSOrders()
console.log(data)
console.log(trades)
})
.catch((err) => {
console.error(err)
})
}
(
postOrder(),
async () => {
const trades = await getTrades()
const mOrders = await getSOrders()
const sells = mOrders.data.asks
const buys = mOrders.data.bids
while (true/*while order is in */) {
trade = trades.data[trades.data.length - 1]
console.log(sells)
}
}
)()
Your code is stuck in a an infinite while loop. Since node is single threaded, it will continue to execute the loop until broken and never execute the .then code in the original promise. You should be able to easily see this by commenting out the while loop.
Specifically:
binanceRest.newOrder is async call. It's true that postOrder is called first, but depending on the how nodejs decides to optimize, all of the awaits are hit at the same time. When the awaits resolve, if Nodejs decides that postOrders will resume your console.log will write, however if the while loop it hit first, then your program will be stuck there.
I'm sorry to say this, but I can see a lot of confusing things in this code.
while(true) {
trade = trades.data[trades.data.length - 1];
console.log(sells);
}
this will never exits. If you omitted part of your code is not a good idea as this makes us harder to give you the actual answer to your question. If this is your real code, I would change in
while(trades.data.length) {
trade = trades.data.pop();
console.log(sells);
}
postOrder is an async function which deals with Promise, and this is confusing: I would rewrite it as
const postOrder = async() => {
try {
const binanceRest = new api.BinanceRest({ ... });
const data = await binanceRest.newOrder({ ... });
const trades = await getTrades()
const mOrders = await getSOrders()
console.log(data)
console.log(trades)
}
catch(err) {
console.error(err)
}
}
Last postOrder is an async function which is called without await and this is source of confusion as well.
I would start cleaning your code, probably many problem will be solved as well.
Hope this helps.
Is not an "await" problem
I've added a bunch of console.log instructions at your code and there's no problem printing anything. Try adding the same to your original code, assuming your API's are working fine there's no reason for failure.
The following snippet mocks your API's, please take a look at the result. Your error must be at your logic, api's or syntax.
Also add a safe break inside your while, something like this: if (calls++ > 5) { break; }, to me it seems that the infinite loop is the problem.
/* MOCK API'S */
let test = 0, calls = 0;
const fakePromise = (arg, apiName) => { return { then: (f1, f2) => { console.log('mocked result for ' + apiName); return f1(arg); } }; };
const axios = (args) => { console.log('-> axios is called', args); return fakePromise({data: { asks: ['ask'], bids: ['bid'], length: 1, 0: 'fake index'} }, args.url); };
const api = { BinanceRest: function(){ return { newOrder: () => fakePromise(' -> newOrder result <- ', 'newOrder')}; } };
const bUrl = 'bUrl/', mOrderQuery = 'mOrderQuery', tradeQuery = 'tradeQuery';
/* YOUR CODE STARTS HERE */
var trade;
const getTrades = async () => {
console.log('-> getTrades is called');
return await axios({
method: 'get',
url: bUrl + tradeQuery
});
}
const getSOrders = async () => {
console.log('-> getSOrders is called');
return await axios({
method: 'get',
url: bUrl + mOrderQuery
})
}
const postOrder = async() => {
console.log('step 1');
const binanceRest = new api.BinanceRest({
// ...
});
console.log('step 2');
binanceRest.newOrder({
// ...
})
.then(async(data) => {
console.log('step 3');
const trades = await getTrades()
const mOrders = await getSOrders()
console.log('step 4.2');
console.log('data: ', data)
console.log('trades: ', trades)
console.log('mOrders', mOrders)
})
.catch((err) => {
console.error(err)
})
}
// test
(
postOrder(),
async () => {
console.log('step 4.1');
const trades = await getTrades()
const mOrders = await getSOrders()
console.log('step 5', mOrders);
const sells = mOrders.data.asks
const buys = mOrders.data.bids
console.log('step 5.0');
while (true/*while order is in */) {
console.log('step 5.' + (test++));
trade = trades.data[trades.data.length - 1]
console.log('sells: ', sells)
if (calls++ > 5) {
console.log('way too much calls, break! ');
break;
}
}
}
)()
Take a closer look at your postOrder func:
const postOrder = async() => {
const binanceRest = new api.BinanceRest({ ... })
binanceRest.newOrder({ ... })
.then(...)
.catch(...)
}
Now imagine the scenario when a typo has been made, f.e. it should be new api.FinanceRest(...) and as a result you end up with error:
Uncaught (in promise) TypeError: api.BinanceRest is not a function
Rest of code in postOrder is simply skipped, no console.logs. So the main point here: if in new api.BinanceRest({ ... }) something leads to an error (any error) than you'll end up with situation you have right now.
And also worth to mention that an async anonymous function with while loop still gets executed and that's because postOrder is an async func which means it returns a pending Promise.
Try running this in you browsers console to get more sense of what's goin' on:
function delay(sec) {
return new Promise((res, rej) => {
setTimeout(() => res("delay " + sec + "sec"), sec * 1000);
});
}
async function getTrades() {
return await delay(0.1);
};
async function getSOrders () {
return await delay(0.2);
};
async function postOrder() {
console.log("postOrder");
const api = {};
const binanceRest = api.BinanceRest()
delay(0.4)
.then(async (data) => {
console.log("postOrder result")
const trades = await getTrades()
const mOrders = await getSOrders()
console.log(trades)
console.log(mOrders)
})
.catch(err => {
console.error(err);
});
};
(postOrder(),
async () => {
const trades = await getTrades();
const mOrders = await getSOrders();
console.log("gfdfg");
})();

How to fix "Function returned undefined, expected Promise or value"

What I am trying to accomplish in a Firebase Function:
Read from Firebase database email addresses to batch email.
Loop through each one and send out emails.
I am having an issue with closing promises I believe. These don't need to be ran in order, I just need to resolve all promises before ending.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const https = require('axios');
exports.sendEmail = functions.pubsub.topic('nightly_topic').onPublish(() => {
let emailsToSend = [];
async function getTests (){
admin.firestore()
.collection("tests")
.get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
emailsToSend.push(doc.data())
});
})
.catch(function (error) {
console.error(error);
});
}
async function send (address){
let body = {
//MANDRILL INFO REMOVED
};
let endpoint = 'https://mandrillapp.com/api/1.0/messages/send-template.json';
https.post(endpoint, body)
.then((result) => {
console.log('SUCCESS');
})
.catch(function (error) {
console.error(error);
});
}
async function init() {
await getTests();
for (const email of emailsToSend) {
await send(email.address);
}
}
init();
});
You're missing return statements from your functions.
return https.post(... etc
Try this:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const https = require('axios');
exports.sendEmail = functions.pubsub.topic('nightly_topic').onPublish(() => {
let emailsToSend = [];
function getTests (){
return new Promise((resolve, reject) => {
admin.firestore()
.collection("tests")
.get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
emailsToSend.push(doc.data())
});
resolve(emailsToSend);
})
.catch(function (error) {
reject(error)
});
});
}
async function send (address){
let body = {
//MANDRILL INFO REMOVED
};
let endpoint = 'https://mandrillapp.com/api/1.0/messages/send-template.json';
https.post(endpoint, body)
.then((result) => {
console.log('SUCCESS');
})
.catch(function (error) {
console.error(error);
});
}
async function init() {
const emailsToSend = await getTests();
for (const email of emailsToSend) {
await send(email.address);
}
}
init();
});
It might help you.
so you are almost there. The issue is that you are not returning anything. You should fix it by just doing:
Return Promise
// code ...
exports.sendEmail = functions.pubsub.topic('nightly_topic').onPublish(() => {
// code
return init();
}
Use async
// code ...
exports.sendEmail = functions.pubsub.topic('nightly_topic').onPublish(async () => {
// code
await init();
}
Note: an async function always returns a Promise.
Suggestion
In your code, you are sending emails one at a time. await send(email.address); This line of code waits until the email is sent, before sending the next one, which is not efficient.
My suggestion is sending all emails at the same time and return a promise that resolves when every email is sent. That should look something like this:
//change this
for (const email of emailsToSend) {
await send(email.address);
}
// --------------into this--------------------------------
//This is an array of promises
const promises = emailsToSend.map(email => {
return send(email.address);
});
await Promise.all(promises);
Hope that helps :)

I get Promise { <pending> } as returned value and also calling in async scope gives me undefined immediately

Im trying to return a value from a Promise in async-await form and use it in another function in another file, but I do have problem because my Promise doesnt return any value.
When im trying to console.log('website') it returns me undefined immediately (it's like the value is not being fetched at all from API services). I dont know what im doing wrong, I really love to learn about Promises and Async-Await but each time im trying to work with them im getting more confused.
const dns = require('dns')
const iplocation = require("iplocation").default;
const emojiFlags = require('emoji-flags');
const getServerIPAddress = async (server) => {
return new Promise((resolve, reject) => {
dns.lookup(server, (err, address) => {
if (err) throw reject(err);
resolve(address);
});
});
};
const getServerLocation = async (server) => {
const ip = await getServerIPAddress(server)
iplocation(ip).then((res) => {
const country = emojiFlags.countryCode(res.countryCode)
const result = `Location: ${country.emoji} ${country.name}`
return result
})
.catch(err => {
return `Location: Unknown`
});
}
(async function() {
console.log(await getServerLocation('www.google.com'))
})()
module.exports = {
getServerLocation
}
It is really important for me to get result from this function first, then use its value in another function. I wish you could give me tips on how to do tasks asynchronously.
You're clearly using async so it's not apparent why you're using then as well. If you use then then you must return the promise as well in order to preserve the promise chain:
const getServerLocation = async (server) => {
const ip = await getServerIPAddress(server)
return iplocation(ip).then((res) => {
const country = emojiFlags.countryCode(res.countryCode)
const result = `Location: ${country.emoji} ${country.name}`
return result
})
.catch(err => {
return `Location: Unknown`
});
}
Otherwise just async this:
const getServerLocation = async (server) => {
const ip = await getServerIPAddress(server)
let res = await iplocation(ip);
const country = emojiFlags.countryCode(res.countryCode)
const result = `Location: ${country.emoji} ${country.name}`
return result
}
const getServerLocation = async (server) => {
const ip = await getServerIPAddress(server)
//you need to return
return iplocation(ip).then((res) => {
const country = emojiFlags.countryCode(res.countryCode)
const result = `Location: ${country.emoji} ${country.name}`
return result
})
.catch(err => {
return `Location: Unknown`
});
}

JS return doesn't wait for await [duplicate]

This question already has answers here:
Using async/await with a forEach loop
(33 answers)
Closed 4 years ago.
I am trying to use the async await.
My app starts like this
axios.get(`${ROOT_URL}/ccidxann.php`)
.catch(err => console.error('axios error get', err))
.then(async res => {
const html = res.data
const jobList = await getJobList(html)
console.log(' after each jobList', jobList)
// jsonfile.writeFileSync('jobs.json', jobList, { flag: 'a' })
})
.catch(err => console.error(err))
The problem is that jobList is always returned as an empty array.
Here is the getJobList function
async function getJobList (html) {
const jobList = []
const dom = new JSDOM(html)
const { window } = dom
const $ = require('jquery')(window)
const jobsInDom = $('table').first().find('td > a')
// AWAIT HERE
await jobsInDom.each(async function (index, jobElem) {
const name = $(jobElem).text()
const url = $(jobElem).attr('href')
// Another AWAIT HERE
const jobDetailsHTML = await getJobDetailsHTML(`${ROOT_URL}/${url}`)
.catch(err => console.error('getJobDetailsHTML err', err))
const domDetails = new JSDOM(jobDetailsHTML)
const { window: windowDetails } = domDetails
const $details = require('jquery')(windowDetails)
const jobDetailsHTMLBody = $details('body').prop('outerHTML')
jobList.push({
_id: url,
name,
detailsHTML: jobDetailsHTMLBody
})
console.log('in each jobList', jobList)
}) //each
return jobList
}
As you can see I put the async keyword in front of it,
and I have put the async keyword in the callback of each
and put await in front of all async operations.
The console.log in the callback of each prints the array filled with values
but it seems the return works before the each
even with await keyword in front of it.
And also here is getJobDetailsHTML just in case. It's a simple function and seems to work just fine
async function getJobDetailsHTML (url) {
// ANOTHER AWAIT HERE
return await axios.get(url).data
}
I think jobsInDom.each is synchronous function, so putting await before doesn't give you desired effect. So somehow you need to get promise which resolved when processing is finished for all jobs, like this:
// handles one element and returns promise
const jobHandler = async jobElem => {
const name = $(jobElem).text()
const url = $(jobElem).attr('href')
const jobDetailsHTML = await getJobDetailsHTML(`${ROOT_URL}/${url}`)
.catch(err => console.error('getJobDetailsHTML err', err))
const domDetails = new JSDOM(jobDetailsHTML)
const { window: windowDetails } = domDetails
const $details = require('jquery')(windowDetails)
const jobDetailsHTMLBody = $details('body').prop('outerHTML')
jobList.push({
_id: url,
name,
detailsHTML: jobDetailsHTMLBody
})
console.log('in each jobList', jobList)
}
const promises = [];
// start processing of each element
jobsInDom.each((index, jobElem) => promises.push(jobHandler(jobElem));
// wait for processing of all job elements
await Promise.all(promises);

Categories