node-csvtojson icon indicating copy to clipboard operation
node-csvtojson copied to clipboard

Async / Await returning Promise <pending> instead of values

Open axdyng opened this issue 6 years ago • 22 comments

Hi,

There seems to be an issue with Async/Await handling. Code below:

async function getData() {
  console.log('logging');
  const test = await CSVToJSON().fromFile('./data/hans-puns.csv');

  return test;
}

const data = getData();

console logging data shows a Promise

axdyng avatar Sep 24 '18 04:09 axdyng

async functions return promises. you need to do const data = await getData() -- unless you intend to set data at the top level of the code, in which case you can't await on getData and need to console.log from inside getData, or log from a different function that calls await getData() inside it.

splichte avatar Sep 24 '18 04:09 splichte

i did not await for getData(). It is done inside the async function.

axdyng avatar Jan 25 '19 04:01 axdyng

I suggest reading a tutorial on async/await, for example: https://medium.com/@_bengarrison/javascript-es8-introducing-async-await-functions-7a471ec7de8a

async functions always return promises.

you need to await to get the value.

splichte avatar Jan 25 '19 22:01 splichte

@splichte I get errors (backed up by documentation) if I attempt to use await outside of an async function. I see some articles claiming you can await anything that returns a promise, without async, but that's not what I'm seeing with NodeJS & ES8.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await

intel352 avatar Oct 02 '19 13:10 intel352

I think I might have been unclear in my earlier comment. I never wanted you to try to await outside an async function. You can't do that in current Node. What you need to do is wrap your await call inside an async function, and then call that async function in the top-level of your script. If you just try to immediately use the output of the async function, it isn't going to be useful, because it's going to be a Promise. But I think people got the impression that I was suggesting to use the given code as-is but only adding await, which is not what I meant to say at all.

You're trying to do something like this:

const data = asyncFunc();
console.log(data); // will give you something like Promise { }

You read my comment and understandably thought I meant to do this:

const data = await asyncFunc(); // will error
console.log(data);

What I actually meant was to do this:

async function run() {
  const data = await asyncFunc();
  console.log(data); // will print your data
}

run() // run the async function at the top-level, since top-level await is not currently supported in Node

You don't need to await on the final run() call, since Node won't exit until its event loop is empty.

splichte avatar Oct 02 '19 17:10 splichte

Hello splichte. Do you want to say, that there is no option how to jump up from async function and load data from async method to some variable for usage in future non-async code? Let`s say, i need to collect data from multiple APIs in paralel, afterwards i need to do some logic on top of collected data. There is no option how to do so?

So in theory: async function run() { const data = await asyncFunc(); //console.log(data); // i don`t want to output anything }

const myVar = run()

Will this load run() output to myVar? I though not, how to achieve that?

petko3000 avatar Oct 04 '19 11:10 petko3000

no, it won't output what you want to myVar, because run is marked async, so it returns a Promise. the main way to force an async function to yield its value is to await on it, which you can only do inside another async function.

you say:

Let`s say, i need to collect data from multiple APIs in paralel, afterwards i need to do some logic on top of collected data. There is no option how to do so?

How you do that is by awaiting on them, then doing the logic inside the async function you've defined. like this:

async function run() {
const data = await asyncFunc();
// Your logic goes here.
}

If you have multiple API calls in parallel, you may want to do something like await Promise.all([asyncCall1(), asyncCall2(),...]). This way you don't have to wait for each function to complete before starting the next. This works because async functions return promises.

You might be under the assumption that at some point you need to "jump" from an async function to a "normal" function for your program to work. You never need to do that. Rather than trying to force the async parts of your program to be synchronous, you should instead incorporate the non-async parts of your program into this async paradigm, which is more in-line with what Node is good at (simultaneous non-blocking tasks).

splichte avatar Oct 04 '19 17:10 splichte

Hello @splichte, I'm struggling a bit trying to understand how async/await works. I have the following piece of code:

export const AddExpense = (data) => (dispatch) => {
	return axios.post('http://localhost:4000/api/Expenses/', data).then((response) => {
		console.log('expid', response.data.id);
		let catId = async () => {
			let res = await axios
				.get('http://localhost:4000/api/Expenses/${id}/getExpenseCategory')
				.then((response) => {
					return response.data;
				});
		};
		let result = await catId;
		console.log('result', result);
		debugger;
		let data = { ...response.data, ...res.data };
		return dispatch({ type: ADD_EXPENSE, payload: data });
	});
};

I need the value that returns axios.get to merge it with what the axios.post request returns before dispatching the merged data itself. The issue that I have is that I don't know how to retrieve the axios.get response. Thank you!

avalladaresm avatar Oct 04 '19 18:10 avalladaresm

As a first note, if you're struggling to understand how async/await/Promises work (which is fair, they can be confusing at first), it might be a good idea to experiment with simpler functions. the code you've sent is fairly dense and has nested lambda functions, mixed Promise-chaining and async/await syntax, calling out to some kind of redux-esque data store, etc. To be honest I find it a little difficult to quickly read and figure out what's going on.

so a thing to realize is that async/await is a way to avoid Promise-chaining syntax -- such as the then statements you're using, which can produce code that's difficult to understand. both await and .then() are ways of handling promises. so, you have this code:

		let catId = async () => {
			let res = await axios
				.get('http://localhost:4000/api/Expenses/${id}/getExpenseCategory')
				.then((response) => {
					return response.data;
				});
		};
		let result = await catId;

Right now, it looks like that top-level lambda async () => { } isn't returning anything. you're setting let res but then not doing anything with it. Also, that response variable you have in the then() statement is already defined in the outer-scoped axios.post function, which is the type of thing that can cause subtle bugs.

You should be able to do something like this:

// post the data (note I've marked the function async -- it's doing external requests, so it should be asynchronous)
const AddExpense = async (data) => {
    // optimization: await on these functions simultaneously with a Promise.all(), which will be faster.
    const postResponse = await axios.post('http://localhost:4000/api/Expenses/', data);

    const getResponse = await axios.get('http://localhost:4000/api/Expenses/${id}/getExpenseCategory');

    const mergedResponses = { ...getResponse.data, ...postResponse.data };

    return dispatch({ type: ADD_EXPENSE, payload: mergedResponses });
}

the benefit of the async/await syntax is to avoid doing difficult-to-parse Promise-chaining like you've done in your example above.

splichte avatar Oct 04 '19 18:10 splichte

I'm sorry I didn't explain it before but I'm glad you still helped me. That worked just as I need it to, thank you! I'll be reading more on the subject to understand it better.

avalladaresm avatar Oct 04 '19 20:10 avalladaresm

This is not even an issue. It is simply lack of understanding of async/await. Please someone close this issue. @splichte Kudos for your patience and explanation. A question like this over stack-overflow would have grilled the OP.

Dr-Xperience avatar Oct 17 '19 17:10 Dr-Xperience

async function run() { const data = await asyncFunc(); console.log(data); // will print your data }

run() var result = data // How to assign resultant value to another variable?

How to get the value (data) outside the block to anothe variable? @splichte

KoushikGoramane avatar Jan 02 '20 11:01 KoushikGoramane

getABC(req);

async function getABC(req){

    let A = await getA(req);
    let B = await getB(req);
    let C = await getC(req);
    
}

console.log('outside function')

when i am calling getABC(req) method, it doesn't wait for the function to be completed. It directly prints outside function. First I want to get all the three values and then it should print outside function

deeratra avatar Jan 10 '20 14:01 deeratra

Hii. I'm begginer and i have this issue when i was trying to connect with Google SpreadSheet: "Promise { }"

I need to use the data "data.lenght" outside the async function. How can i do it?

` async function accessSpreadsheet() {

await promisify (doc.useServiceAccountAuth)(creds);

const info = await promisify(doc.getInfo)();

const sheet = info.worksheets[0]; 

const data = await promisify(sheet.getRows)({                                                                       
    query: `data = ${datainput}`   
});

return data.length;

}

var total = accessSpreadsheet(); console.log(total); `

tiagotozi avatar Jan 26 '20 16:01 tiagotozi

The suggested advice by @splichte about reading a tutorial is the best way to go. There's really no way to give short easy answers to a concept that has to be really grasped and understood. Tutorial here

impactcolor avatar Jan 29 '20 18:01 impactcolor

my async function is returning something like this: Promise { 'value' }. I evaluated it and it came up isPending. Is it possible to get a value out of this?

marcguvenc99 avatar Apr 06 '20 06:04 marcguvenc99

i got it, I needed a .then statement, thx

marcguvenc99 avatar Apr 06 '20 07:04 marcguvenc99

You need to use await on data, here's my solution:

`(async function load(){ async function getData() { console.log('logging'); const test = await CSVToJSON().fromFile('./data/hans-puns.csv');

return test; } const data = await getData(); })()`

KRIMINALinc avatar May 25 '20 12:05 KRIMINALinc

async function getData() { console.log('logging'); const test = await CSVToJSON().fromFile('./data/hans-puns.csv');

return test; }

if you want to be able to return a value you will have to put an await in front of your function

const data = await getData();

now if you call the function in a get router you have to put an async as follows

router.get('/routerNme,async (req, res) => {

console.log(await getData()) });

I hope that could help you @dysol

kambelkouame avatar Aug 12 '20 10:08 kambelkouame

I am having the same issue. Can someone tell what am I doing wrong? ` const axios = require('axios');

const ageFinder = async (userName) => {

try {
    let userDetails =await axios.get(`https://hacker-news.firebaseio.com/v0/user/${userName}.json`).then(res => res.data.created)
    let user =userDetails.data
    return user
} catch(err) {
    console.log(err.message)
}

}

console.log(ageFinder("someUser"))`

Karan-Chhajed avatar Oct 01 '20 22:10 Karan-Chhajed

For getting something from API:

router.get("/list", async (req, res) => {
  getData = async () => {
    const test = await axios.get("https://jsonplaceholder.typicode.com/posts");
    return test;
  };

  const data = await getData();

  res.send(data.data);
});

In your case you can do something like this:

async function getData() {
  console.log('logging');
  const test = await CSVToJSON().fromFile('./data/hans-puns.csv');
  return test;
}

const data = await getData();
console.log(data);

nijatmursali avatar Nov 09 '20 01:11 nijatmursali

I am also struggling with this seemingly most basic task. I want to call a function that returns some data, assign that data to another variable, and only then continue with execution. I have read the tutorial that @splichte mentioned but it did not address the issue. Consider the code below, based on splichte's suggestion:

async function run() { const data = await asyncFunc(); return data }

console.log('Getting mydata...') mydata = run() console.log('My data is', mydata) process.exit()

When I run this the output is

Getting mydata... mydata is Promise {[[PromiseState]]: 'pending',...

So it hasn't waited for the result, as expected. If I add the await keyword before the call to run() then I get the error "await is only valid in async functions and the top level bodies of modules". I thought that creating the run() function at the top-level was the way to solve that?

Assuming that the function asyncFunc() eventually returns 'Hello World' what code must be written to get the expected output below?

Getting mydata... mydata is Hello World

(P.S. apologies for using Quote instead of Code. The code formatting did not work, running multiple lines together.

Eyles-IT avatar Aug 22 '23 19:08 Eyles-IT