Async/Await on Callback not waiting - javascript

I am querying AMPS SOW using javascript API. My functions look like this:
sow_query = async(topic, filter, options) => {
await this.connection
await this.client.sow(this.onMessage, topic, filter, options)
return this.message
}
onMessage = (message)=>{
this.message.push(message.data)
}
//Calling functions
const getData = async(date) =>{
let data = await getAmpsData(date)
}
async getAmpsData(date){
const filter = "some filter"
const data = await ampsClient.sow_query(topic, filter)
data.forEach((element) => console.log(element))
}
It looks like my call to ampsClient.sow_query doesnt wait for the callback to finish so returns an empty list. Therefore, I cannot loop through the data in my calling function getAmpsData.
I know it has to do with async/await because if I simply do console.log(data) in getAmpsData i can see the data (perhaps after 2 seconds) when the promise is resolved. Is there anyway i can get this working?

If I understand you correctly, data in getAmpsData is an array as expected, but data in getData is undefined. It's not an async/await problem, you just forgot to add return data; to getAmpsData.

I not sure about what package you are using. But, maybe, it using a callback function to get the result of .sow function - message.data.
With your logic, onMessage function will be called after data.forEach done, you can try adding a console.log line to onMessage function.
Maybe, the package has an important reason to do that. But, to fix this issue, you can wrap .sow function into a promise.
sow_query = async (topic, filter, options) => {
await this.connection // ???
return new Promise((resolve) => { // wrap in a promise
this.client.sow( // no need to wait .sow
(message) => {
resolve(message.data); // just resolve when we get the message's data
},
topic, filter, options)
});
}
//Calling functions
const getData = async (date) => {
let data = await getAmpsData(date)
}
async getAmpsData(date) {
const filter = "some filter"
const data = await ampsClient.sow_query(topic, filter)
data.forEach((element) => console.log(element))
}

Related

Why can I not return an array of objects in an async function?

In node.js I am trying to get a list of bid and ask prices from a trade exchange website (within an async function). Within the foreach statement I can console.info() the object data(on each iteration) but when I put all of this into an array and then return it to another function it passes as 'undefined'.
const symbolPrice = async() => {
let symbolObj = {}
let symbolList = []
await bookTickers((error, ticker) => {
ticker.forEach(symbol => {
if (symbol.symbol.toUpperCase().startsWith(starts.toUpperCase())) {
symbolObj = {
symbol: symbol.symbol,
bid: symbol.bidPrice,
ask: symbol.askPrice
}
console.info(symbolObj);
}
symbolList.push(symbolObj)
});
const results = Promise.all(symbolList)
return results;
});
}
const symbolPriceTest = async() => {
const res = await symbolPrice(null, 'ETH', true);
console.log(res)
}
I have tried pretty much everything I can find on the internet like different awaits/Promise.all()'s. I do admit I am not as familiar with async coding as I would like to be.
So, if the basic problem here is to call bookTickers(), retrieve the asynchronous result from that call, process it and then get that processed result as the resolved value from calling symbolPrice(), then you can do that like this:
const { promisify } = require('util');
const bookTickersP = promisify(bookTickers);
async function symbolPrice(/* declare arguments here */) {
let symbolList = [];
const ticker = await bookTickersP(/* fill in arguments here */);
for (let symbol of ticker) {
if (symbol.symbol.toUpperCase().startsWith(starts.toUpperCase())) {
symbolList.push({
symbol: symbol.symbol,
bid: symbol.bidPrice,
ask: symbol.askPrice
});
}
}
return symbolList;
}
async function symbolPriceTest() {
const res = await symbolPrice(null, 'ETH', true);
console.log(res)
}
Things to learn from your original attempt:
Only use await when you are awaiting a promise.
Only use Promise.all() when you are passing it an array of promises (or an array of a mixture of values and promises).
Don't mix plain callback asynchronous functions with promises. If you don't have a promise-returning version of your asynchronous function, then promisify it so you do (as shown in my code above with bookTickersP().
Do not guess with async and await and just throw it somewhere hoping it will do something useful. You MUST know that you're awaiting a promise that is connected to the result you're after.
Don't reuse variables in a loop.
Your original implementation of symbolPrice() had no return value at the top level of the function (the only return value was inside a callback so that just returns from the callback, not from the main function). That's why symbolPrice() didn't return anything. Now, because you were using an asynchronous callback, you couldn't actually directly return the results anyway so other things had to be redone.
Just a few thoughts on organization that might be reused in other contexts...
Promisify book tickers (with a library, or pure js using the following pattern). This is just the api made modern:
async function bookTickersP() {
return new Promise((resolve, reject) => {
bookTickers((error, ticker) => {
error ? reject(error) : resolve(ticker);
})
});
}
Use that to shape data in the way that the app needs. This is your app's async model getter:
// resolves to [{ symbol, bid, ask }, ...]
async function getSymbols() {
const result = await bookTickersP();
return result.map(({ symbol, bidPrice, askPrice }) => {
return { symbol: symbol.toUpperCase(), bid: bidPrice, ask: askPrice }
});
}
Write business logic that does things with the model, like ask about particular symbols:
async function symbolPriceTest(search) {
const prefix = search.toUpperCase();
const symbols = await getSymbols();
return symbols.filter(s => s.symbol.startsWith(prefix));
}
symbolPriceTest('ETH').then(console.log);

Array index returns undefined after push - Papa Parse

I used Papa Parse to parse a .csv file, and pushed the result to an empty array called parsed_data. I am able to use console.log(parsed_data), and view the arrays produced. However, when I try to index the data, for example, console.log(parsed_data[0]), the result is undefined. Not sure what's going wrong here.
Example code:
let parsed_data = [];
const data_url = "acdata.csv";
async function getData() {
const response = await fetch(data_url);
const blob = await response.blob();
const data = Papa.parse(blob, {
complete: function(results) {
//console.log("Finished:", results.data);
parsed_data.push(results.data);
}
});
};
console.log(parsed_data);
getData();
Since Papa parse complete callback is called asynchronously, you'll need to wait for that to complete - however, papa parse doesn't seem to use Promises, so, you can "promisify" the parse function like so
const papaParsePromise = blob => new Promise(resolve => Papa.parse(blob, { complete: resolve }));
Another way of looking at that function, if you don't understand the => notation, is
function papaParsePromise(blob) {
return new Promise(function(resolve) {
Papa.parse(blob, {
complete: function(data) {
resolve(data);
}
);
});
}
that returns a promise that resolves to the data that is passed to the complete callback
Your code would also need to wait for the promise returned by getData before it can use anything in that data. Unless your code is inside another async function, you'll need to use promise .then method, as below
const data_url = "acdata.csv";
async function getData() {
// create a function that returns a Promise that resolves when papa parse complete is called
const papaParsePromise = blob => new Promise(resolve => Papa.parse(blob, { complete: resolve }));
const response = await fetch(data_url);
const blob = await response.blob();
const data = await papaParsePromise(blob);
return data;
};
getData()
.then(parse_data => {
console.log(parsed_data);
console.log(parsed_data[0]);
// i.e. do what you need with parsed_data here
});
if, however, you are calling getData inside an async function - along with the changes to getData above, you can simply use await getData() to wait for the value - i.e.
async function someFunction() {
const parsed_data = await getData();
// do what you need with parsed_data here
}

How to get fetch api results in execution order with async/await?

After an input change in my input element, I run an empty string check(if (debouncedSearchInput === "")) to determine whether I fetch one api or the other.
The main problem is the correct promise returned faster than the other one, resulting incorrect data on render.
//In my react useEffect hook
useEffect(() => {
//when input empty case
if (debouncedSearchInput === "") autoFetch();
//search
else searchvalueFetch();
}, [debouncedSearchInput]);
searchvalueFetch() returned slower than autoFetch() when I emptied the input. I get the delayed searchvalueFetch() data instead of the correct autoFetch() data.
What are the ways to tackle this? How do I queue returns from a promises?
I read Reactjs and redux - How to prevent excessive api calls from a live-search component? but
1) The promise parts are confusing for me
2) I think I don't have to use a class
3) I would like to learn more async/await
Edit: added searchvalueFetch, autoFetch, fetcharticles code
const autoFetch = () => {
const url = A_URL
fetchArticles(url);
};
const searchNYT = () => {
const url = A_DIFFERENT_URL_ACCORDING_TO_INPUT
fetchArticles(url);
};
const fetchArticles = async url => {
try{
const response = await fetch(url);
const data = await response.json();
//set my state
}catch(e){...}
}
This is an idea how it could looks like. You can use promises to reach this. First autoFetch will be called and then searchvalueFetch:
useEffect(() => {
const fetchData = async () => {
await autoFetch();
await searchvalueFetch();
};
fetchData();
}, []);
You can also use a function in any lifecycle depends on your project.
lifecycle(){
const fetchData = async () => {
try{
await autoFetch();
await searchvalueFetch();
} catch(e){
console.log(e)
}
};
fetchData();
}
}

Save Async/Await response on a variable

I am trying to understand async calls using async/await and try/catch.
In the example below, how can I save my successful response to a variable that can be utilized throughout the rest of the code?
const axios = require('axios');
const users = 'http://localhost:3000/users';
const asyncExample = async () =>{
try {
const data = await axios(users);
console.log(data); //200
}
catch (err) {
console.log(err);
}
};
//Save response on a variable
const globalData = asyncExample();
console.log(globalData) //Promise { <pending> }
1) Return something from your asyncExample function
const asyncExample = async () => {
const result = await axios(users)
return result
}
2) Call that function and handle its returned Promise:
;(async () => {
const users = await asyncExample()
console.log(users)
})()
Here's why should you handle it like this:
You can't do top-level await (there's a proposal for it though);
await must exist within an async function.
However I must point out that your original example doesn't need async/await
at all; Since axios already returns a Promise you can simply do:
const asyncExample = () => {
return axios(users)
}
const users = await asyncExample()
try..catch creates a new block scope. Use let to define data before try..catch instead of const, return data from asyncExample function call
(async() => {
const users = 123;
const asyncExample = async() => {
let data;
try {
data = await Promise.resolve(users);
} catch (err) {
console.log(err);
}
return data;
};
//Save response on a variable
const globalData = await asyncExample();
console.log(globalData);
// return globalData;
})();
I had same issue with you and found this post. After 2 days of trying I finally found a simple solution.
According to the document of JS, an async function will only return a Promise object instead of value. To access the response of Promise, you have to use .then()method or await which can return the resulting object of Promise is instead of Promise itself.
To change variables from await, you have access and change the variable you want to assign within the async function instead of return from it.
//Save response on a variable
var globalData;
const asyncExample = async () =>{
try {
const data = await axios(users);
globalData = data; // this will change globalData
console.log(data); //200
}
catch (err) {
console.log(err);
}
};
asyncExample();
But if you do this, you may get an undefined output.
asyncExample();
console.log(globalData) //undefined
Since asyncExample() is an async function, when console.log is called, asyncExample() has not finished yet, so globalData is still not assigned. The following code will call console.log after asyncExample() was done.
const show = async () => {
await asyncExample();
console.log(globalData);
}
show();
Because the events are happening asynchronously you need to tie in a callback/promise. I'm going to assume it returns a promise.
const axios = require('axios');
const users = 'http://localhost:3000/users';
const asyncExample = async () =>{
try {
const data = await axios(users);
console.log(data); //200
}
catch (err) {
console.log(err);
}
};
//Save response on a variable
const globalData = asyncExample().then( (success, err) => {
if (err) { console.error(err); }
console.log(success)
}
Just use a callback/promise (cascading programming):
axios(users).then(function(response) {
const globalData = response;
console.log(globalData)
});

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