0

I'm putting my API calls in a new function so I can easily call them with a couple of lines instead of a ton of code on every page. The problem I'm having is that the async and await is completing before returning the data.

I'm getting console.log(sess.getIdToken().getJwtToken()) consoled first, followed by undefined for console.log(getMessages), then the data from console.log(response).

So the await Auth.currentSession().then(...) runs but does't wait for the axios call inside of it. How do I return the data so I can access it in the useEffect?

useEffect(() => {
    async function getMessages() {
        const getMessages = await ApiService.getMessages();
        console.log(getMessages)
    }
    getMessages();
}, []);

async getMessages() {
    return await this.__sendRequest("/default/message", "GET");
}

async __sendRequest(url, method) {
    await Auth.currentSession().then(sess => {
        console.log(sess.getIdToken().getJwtToken())
        axios({
            method: method,
            url: process.env.REACT_APP_USER_URL + url,
            headers: {
                "X-TOKEN-ID": sess.getIdToken().getJwtToken(),
                "addresseeType": "P",
                "content-type": "application/json"
            }
        }).then(function (response) {
            console.log(response)
            return response
        }).catch(err => {
            console.error(err);
        })
    })
}
Matt Brody
  • 1,473
  • 3
  • 14
  • 23

3 Answers3

3

Mixing async/await with promise-chaining (.then(...)) is a really easy way how to overcomplicate your code.

I can see a couple of places that contribute to this not working.

  • In __sendRequest you are not returning anything.
  • in the first .then you are lacking a return too.

Here is a simplified and fixed code

async __sendRequest(url, method) {
    const sess = await Auth.currentSession()

    console.log(sess.getIdToken().getJwtToken())
    
    const response = await axios({
        method: method,
        url: process.env.REACT_APP_USER_URL + url,
        headers: {
            "X-TOKEN-ID": sess.getIdToken().getJwtToken(),
            "addresseeType": "P",
            "content-type": "application/json"
        }
    })

    console.log(response)

    return response
}

Check the comments of the other answer, to understand about async/await, returning and call stack https://stackoverflow.com/a/69179763/1728166

Hendry
  • 882
  • 1
  • 11
  • 27
2

There are a couple of issues:

  1. __sendRequest has no return, so when it's done waiting it will fulfill its promise with undefined.
  2. The promise from the chain on Auth.currentSession() isn't waiting for the axios call to completely because the two chains aren't linked in any way.
  3. (Not the reason you're seeing the premature settlement, but still an issue.) Don't trap errors too early, let them propagate so the caller knows that something went wrong.

Also, in general, don't mix using .then/.catch with async/await, there's no need (generally); just use await:

useEffect(() => {
    async function getMessages() {
        try {
            const getMessages = await ApiService.getMessages();
            console.log(getMessages);
        } catch (error) {
            // ...*** handle/report error...
        }
    }
    getMessages();
}, []);

async getMessages() {
    return await this.__sendRequest("/default/message", "GET");
}

async __sendRequest(url, method) {
    const sess = await Auth.currentSession();
    console.log(sess.getIdToken().getJwtToken());
    return await axios({
        method: method,
        url: process.env.REACT_APP_USER_URL + url,
        headers: {
            "X-TOKEN-ID": sess.getIdToken().getJwtToken(),
            "addresseeType": "P",
            "content-type": "application/json"
        }
    });
}
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 2
    I consider not `await`ing when returning a anti-pattern because, this will resolve in incomplete callstack, which can make debugging a bit harder in complicated applications. Can read about it here https://docs.google.com/document/d/13Sy_kBIJGP0XT34V1CV3nkWya4TwYx9L3Yv45LdGB6Q/edit#heading=h.gfyk64pfqx1c There are differences with catching exceptions as well. Here's a resource about it https://jakearchibald.com/2017/await-vs-return-vs-return-await/ – Hendry Sep 14 '21 at 14:46
  • @Hendry - Yes, I called out the difference with catching exceptions explicitly in the last paragraph. The call stack argument is an interesting one, I'll look into it, thanks. – T.J. Crowder Sep 14 '21 at 14:47
  • 2
    Here's another link about async stack traces https://mathiasbynens.be/notes/async-stack-traces – Hendry Sep 14 '21 at 14:49
  • 2
    @Hendry - Heck, I didn't even need to read them (though I will), a [**very** quick experiment](https://jsfiddle.net/tjcrowder/dy9z2qg4/) tells me: Use `return await`. :-D – T.J. Crowder Sep 14 '21 at 14:51
  • 1
    Am glad you got something out of this! Cheers! – Hendry Sep 14 '21 at 15:03
  • Note that removing `async` in the jsfiddle fixes the callstack issue, which, in my opinion, is an argument against `async/await`, but that's a whole other subject! – Emile Bergeron Sep 14 '21 at 15:07
  • 2
    @EmileBergeron - Fair! Though, that's not an option if the function would contain other function calls needing to be `await`ed – Hendry Sep 14 '21 at 15:09
  • 1
    @EmileBergeron - I actually meant to leave it out, oops. But even then, you get less information: https://jsfiddle.net/tjcrowder/dy9z2qg4/1/ Unless I'm misunderstanding? – T.J. Crowder Sep 14 '21 at 15:10
  • You do get less information, but the information that's left out is unnecessary to debug since you get the relevant callstack, `outerWithout` and `inner`, where `middleWithout` is merely a passthrough, as if it was just part of `outerWithout`. – Emile Bergeron Sep 14 '21 at 15:12
  • Also note that you get less information since `inner` is an async function. If you remove all `async/await` and [just use good old promises](https://jsfiddle.net/aLfuq1ob/), the callstack is complete, the code (in this case) is less noisy, and more predictable. – Emile Bergeron Sep 14 '21 at 15:17
-1

getMessages() is async so you need to call with await and make the callback function of first line async so you can use await inside it.

  useEffect(async () => {
    async function getMessages() {
        const getMessages = await ApiService.getMessages();
        console.log(getMessages)
    }
    await getMessages();
 }, []);

you can simplify the __sendRequest by converting all then() callbacks into await and put the whole chain inside a try/catch block.

pref
  • 1,651
  • 14
  • 24
  • 1
    No, you don't have to call it with `await` if you don't want to wait for the response (which the `useEffect` callback doesn't need to). Also, passing `async` functions as callbacks to things that don't handle the promise or return it to you is generally poor practice (`useEffect` does neither). – T.J. Crowder Sep 14 '21 at 15:06
  • useEffect() is calling getMessages() and that is an async function. He is asking "How do I return the data so I can access it in the useEffect". So he needs to wait for getMessages() inside the useEffect to return the data – pref Sep 15 '21 at 21:51
  • No, he doesn't, `getMessages` does its own state call. You're not using the fulfillment value in the `useEffect` callback, not handling rejection, and not doing other things after the promise settles. `await` is unnecessary there. It would be harmless, but again, `useEffect` won't handle the promise you're returning to it. – T.J. Crowder Sep 16 '21 at 06:11