Asynchronous function call from synchronous function (Node.js) - javascript

I am having trouble with getting access to a response from my asynchronous function. I understand this is a common issue, but I cannot find another question that pertains to my issue.
My Synchronous Function:
const output = getSeasons.getSeasons('*URL*', (data)=>{
return data
})
console.log(data)
My Asynchronous Function:
const getSeasons = async (url, callback) => {
seasonTitle = *ASYNCHRONOUS CALL THAT RETURNS AN ARRAY*
await seasonTitle
callback(seasonTitle)
}
My issue is that I want to be able to continue in my synchronous function utilizing "output" in future lines. The only solution I can think of is:
const output = getSeasons.getSeasons('*URL*', (data)=>{
return data
}).then(()=>{ console.log(data) }
My issue with this option is that all of my future code for this function will have to be written inside the ".then" function. I would much prefer to wait for a response before continuing on in my synchronous function without putting everything in ".then". Mainly because I will have multiple functions calling asynchronous functions and that will result in multiple nested ".then" functions.
EDIT:
Here is the actual code, the asynchronous call is scraping for elements with a class of seasons and then I am trying to return the array:
const output = getSeasons.getSeasons('https://www.rocketleagueesports.com/schedule/', (data)=>{
console.log(data)
})
console.log(output)
const getSeasons = async (url, callback) => {
const browser = await puppeteer.launch({ headless: true })
const page = await browser.newPage()
await page.goto(url)
await page.waitForSelector('.match') //Waits for elements with class "match" to load before executing further
const seasonTitle = page.evaluate(() => {
const seasonTitleArray = Array.from(document.querySelectorAll('.sub-header .scoreboard-container .container-lg .show-on-md .select-options-container .seasons li'))
return seasonTitleArray.map(li => li.textContent)
})
await seasonTitle
callback(seasonTitle)
}

Why not use the await as you have done in getSeasons
const getSeasons = async funciton(url) {
return await *ASYNCHRONOUS CALL THAT RETURNS AN ARRAY*
}
(async () => {
const output = await getSeasons.getSeasons('*URL*')
console.log(output)
})()
async/await

If you don't want to write code in then block, then use async/await
async someFunction() {
const output = await getSeasons.getSeasons(...)
}
Besides that, your getSessions need some improvements. Why do you need both callback and async which return a Promise?
Try to refactor something like this:
const getSeasons = async function(url) {
return *ASYNCHRONOUS CALL THAT RETURNS AN ARRAY*
}

Related

Converting a synchronous function to be async

As I was implementing service worker subscriptions, I noticed a message from Visual Studio Code, that the following function can be converted to be an async function:
Here is the my original synchronous code.
/**
* Returns the subscription if it is present, or nothing
*/
function getUserSubscription() {
return navigator.serviceWorker.ready
.then(function (serviceWorker) {
return serviceWorker.pushManager.getSubscription();
})
.then(function (pushSubscription) {
return pushSubscription;
});
}
I'm not sure how I'd convert this to be an async function. I thought I understood the gist of it, as in converting something like this:
fetchData()
.then(process())
.then(processAgain());
to this:
const response1 = await fetchData();
const response2 = await process(response1);
const response = await processAgain(response2);
But I've had no luck converting my function using this technique.
Use the async keyword in front of the function. Then await each Promise and return the result.
async function getUserSubscription() {
const serviceWorker = await navigator.serviceWorker.ready;
const pushSubscription = await serviceWorker.pushManager.getSubscription();
return pushSubscription;
}

Do I have to await an async function in useEffect that is already async?

Let's say I have the following code below, and I call my function usersaApi.getUsersId it in a useEffect function, do I have to make the response (as it returns the values, not the Promise) an async function?
const usersApi = {
getUserIds: async storyId => {
const result = await axios
.get(`${users + userId}.json`)
.then(({ data }) => data);
return result;
},
}
useEffect(() => {
const fetchData = async () => {
await usersApi.getUserIds().then(data => setUserIds(data));
};
fetchData();
}, []);
What's the difference also between using:
const fetchData = async () => {
await usersApi.getUserIds().then(data => setUserIds(data));
};
vs.
const fetchData = async () => {
const result = await usersApi.getUserIds().then(data => setUserIds(data));
return result;
};
I presume I can just await the response and not return the result right? It's kind of two questions here:
1) Do I have to use async/await on a function that is already async/await
2) The difference between using await & return on a function that already returns data, not promises.
1) Do I have to use async/await on a function that is already async/await
It's up to you what syntax you want to use. You could either use async/await, or you could use the older .then style. Personally i find the async await style easier to understand so i personally prefer it. I would however recommend against mixing the two syntaxes. It's almost never necessary to do so, and it just ends up making the code harder to understand. Either write:
const fetchData = async () => {
const data = await userApi.getUserIds();
setUserIds(data);
}
or:
const fetchData = () => {
userApi.getUserIds.then(data => setUserIds(data));
}
2) The difference between using await & return on a function that already returns data, not promises.
Async/await exists to simplify the syntax for working with promises. If you aren't using promises, you don't need async/await.

How to make `return` respecting async/await in JS?

I'm trying to make this code (a class method) returning a String.
async sign() {
const txt = 'ddddd';
const result = await crypto.createHash('md5').update(txt, 'binary').digest('hex');
return result;
}
The problem is that it ignores await and returns a Promise. This function's returned value is used as a HTTP request header, and while npmlog says it's
apisign: Promise { 'faaa3f1409977cbcd4ac50b5f7cd81ec' }
in network traffic caught by Wireshark I see
apisign: [object Promise]
How do I make return respecting await, or how should I write it so it returns a String?
You should not return the value of an async function as is, since it is a Promise
await for it before serializing it.
An async function always returns a Promise.
If you invoke sign() inside another function then you have to await for it and this requires making the caller function also an async function and so on and so forth.
Eventually, at the top-level code you have to use the regular .then().catch() syntax to wait for the Promise to settle:
sign()
.then((result) => {
// do something with the result (e.g. put it into the response header)
console.log(result);
})
.catch((err) => {
// something wrong happened and the Promise was rejected
// handle the error
console.log(`There was an error: ${err.message || err}`);
});
You will have to await the response of a async function.
const getURL = (title, page) => `https://jsonmock.hackerrank.com/api/movies/search/?Title=${title}&page=${page}`
const callService = async (title, page) => {
let response = await fetch(getURL(title, page));
return await response.json();
}
async function callApi() {
let data = await callService('spiderman', 1);
console.log(data.data.length);
}
callApi();

Export the value returned from an async/await function in ES6

Is it possible to export the value of an await function from an ES6 class? For example, I have a class with a function named getDetails which looks like this
class Config {
static async getDetails() {
let response = await fetch('url/where/details/are')
let data = await response.json()
return data;
}
}
export { Config }
When I import this class into another file, and call User.getDetails(), whats returned is Promise {<pending>}. Is there a way to make the stack wait for a response from the API first?
Why not just use await in your parent file too?
// your async function
const res = await User.getDetails()
// here you have res
Alternatively, you can use .then
User.getDetails()
.then(res => {
// use res here
})
When calling User.getDetails(), you need to either use .then() or use await. Remember – you can only use await within an async function, so you would either do
async myFunction() {
const data = await User.getDetails();
}
or
myFunction() {
const data = User.getDetails().then(res => {})
}

async/ await not waiting for async.map call to be completed

In my Node.js app I'm trying to use a helper function to gather and format some data to ultimately be returned via an API endpoint. I need to loop through an array, and for each entry, make an asynchronous call to my database. However, I'm unable to return this new array of data in my route once I map over it. I believe that this has something to do with the nature of 'async.map' but have been unable to figure it out from the docs. Am I missing something? Better way to do this?
router.get('/route',async (req,res) => {
const formattedData = await module.formatData(data);
// formattedData IS RETURNED AS UNDEFINED HERE
res.json({data:formattedData});
};
Relevant helper functions:
formatData: async(data) => {
// gets entire object from the id that I originally had
getNewData: (dataID) => {
const data = Data.find({_id:dataID},(error,response)=>{
return response;
})
return data;
},
const formatDataHelper = async(data,done) =>{
// THIS function queries database (reason I have to use async)
const newData = await getNewData(data);
done(null, newData);
}
function dataMap(data, callback) {
async.map(data, formatDataHelper, callback);
}
const returnData = await dataMap(data,(err,response)=>{
return response;
})
return returnData;
},
await only awaits something when you await a function that returns a promise that is linked to whatever you want to await. Your dataMap() function returns nothing, therefore await on that function doesn't await anything.
So, in order to work with await, your dataMap() function needs to return a promise that is tied to the asynchronous operation it is doing.

Categories

Resources