Keyang / node-csvtojson

Blazing fast and Comprehensive CSV Parser for Node.JS / Browser / Command Line.
MIT License
2.02k stars 271 forks source link

Async / Await returning Promise <pending> instead of values #278

Open axdyng opened 6 years ago

axdyng commented 6 years ago

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

splichte commented 6 years ago

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.

axdyng commented 5 years ago

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

splichte commented 5 years ago

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.

intel352 commented 5 years ago

@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

splichte commented 5 years ago

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.

petko3000 commented 5 years ago

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?

splichte commented 5 years ago

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).

avalladaresm commented 5 years ago

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!

splichte commented 5 years ago

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.

avalladaresm commented 5 years ago

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.

Dr-Xperience commented 5 years ago

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.

KoushikGoramane commented 4 years ago

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

deeratra commented 4 years ago

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

tiagotozi commented 4 years ago

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); `

impactcolor commented 4 years ago

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

marcguvenc99 commented 4 years ago

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 commented 4 years ago

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

KRIMINALinc commented 4 years ago

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(); })()`

kambelkouame commented 4 years ago

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

Karan-Chhajed commented 4 years ago

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"))`

nijatmursali commented 4 years ago

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);
Eyles-IT commented 1 year ago

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.