0

I just get confused sometimes with the use of async/await. I tried to learn it a lot but I always end up questioning myself. So here is an example. I have a imported function which calls the backend on my react-app to ultimately talk to mongodb. My question is, what is the difference between using:

async function handleChangeSelect(value,action){
  await getOneOrg(value.label).then(res=>{
        const updatedCategory = {...modal, [action.name]:value, categories:[{value:res.ans,label:res.ans}]}
        setModal(updatedCategory)
    }).catch(err=>console.log(err))
}

VS.

function handleChangeSelect(value,action){
   getOneOrg(value.label).then(res=>{
        const updatedCategory = {...modal, [action.name]:value, categories:[{value:res.ans,label:res.ans}]}
        setModal(updatedCategory)
    }).catch(err=>console.log(err))
}

They both seem to work and do the same thing. Like when do I need to use async await (I see people put it on the parent function of a .then. I know fetch/.then is already async so you do not need to but then when do you even need to?). What is the point of putting it in the parent function. I just find myself extremely confused on when to use this option and for what purpose. I need examples, I have a hard time just grasping a concept. Also, what exactly is happening in the hardware/software when you write this?

2
  • async implicitly returns a Promise, so the difference is mainly the function signatures. Commented Nov 18, 2020 at 21:00
  • @zero298 well that and also when either of these actually finishes. First one completes after getOneOrg finishes, the second one before it finishes. Commented Nov 18, 2020 at 21:01

3 Answers 3

1

The thing is that in the first example you are not really using async/await. The code should be:

async function handleChangeSelect(value,action){
  try {
    const res = await getOneOrg(value.label)
    const updatedCategory = {...modal, [action.name]:value, categories:[{value:res.ans,label:res.ans}]}
    setModal(updatedCategory)
  }
catch(err) { console.log(err)}
}

If you have many promises concatenated the use of async-await result in a cleaner and more understandable code.

I don't want to enter in details on the use and the behind the scenes because there are a lot of resources online.

Sign up to request clarification or add additional context in comments.

5 Comments

res retuns a http response. to update the categories he has to write : res.data.ans
I think this is kind of high level for me. I thought async/await and .then() kinda make it into a background process? Like is there an example of not using the .then() method but needing to use async/await?
@smammadov94 async/await are pretty much syntactic sugar over the promise API. There isn't really any await usage that cannot be expressed as the promise API. The majority of promise API usages are also expressible via async/await. The most notable thing you cannot do via await-ing is Promise.all(), Promise.allSettled(), Promise.any(), and Promise.race().
Yes as @VLAZ said is a syntax sugar. There are some differences behind the scenes but in fact the idea of use async/await is to replace .then() If you don't use async/await you can easily fall into a something called "Promise hell"
@Lemon you shouldn't fall into a promise hell. The whole design goal behind promises is to avoid the callback hell pattern by making them chainable and auto-merging/flattening the promises. They aren't the same - if you start nesting the promises, then you are doing something wrong, it's not the fault of promises. Still, that doesn't mean that the .then syntax is always the same. Sometimes, the code is easier to read using await but not because excessive nesting.
0

The first is "incorrect" usage of async/await. From the docs:

An async function is a function declared with the async keyword. Async functions are instances of the AsyncFunction constructor, and the await keyword is permitted within them. The async and await keywords enable asynchronous, promise-based behavior to be written in a cleaner style, avoiding the need to explicitly configure promise chains.

The first example should be along the lines of:

async function handleChangeSelect(value, action) {

  const res = await getOneOrg(value.label).catch(err => console.log(err))

  const updatedCategory = {
    ...modal,
    [action.name]: value,
    categories: [{
      value: res.ans,
      label: res.ans
    }]
  }
  setModal(updatedCategory)
}

This waits for the getOneOrg function to resolve before continuing onto updating the updatedCategory object.

Short answer - it removes the need for chaining .then() everywhere.

9 Comments

"The first example should be along the lines of: [...] await getOneOrg(value.label).catch(err => console.log(err))" ??? Wasn't your point that you shouldn't mix promises API with async/await?
I think I kind of understand but in terms of just fetching. Basically what I am seeing here is that .then() is the same as if you create a function like the one above. So using .then() will still wait for the function to complete and not using it just kinda passes by it and it happens sometime in the background?
This wasn't a comprehensive breakdown of async/await. You can wrap in a try/catch as well.
@smammadov94 it's the same functionality, with less clutter. You can either use then in non-async functions or use await in async functions.
Still a little confused, I don't know why but I cannot wrap my head around it. I kind of want to understand an example where I will use async/await over .then().
|
0

There is a crucial difference between your two examples. It lies how they would handle being called with await. I'll represent the two in simplified terms.

First code block:

const someAsyncOperation = ms => new Promise(res => setTimeout(res, ms, "hello"))

async function foo(){
  console.log("start async");
  await someAsyncOperation(1500)
    .then(res => console.log("do something with result:", res + "world"))
    .catch(() => console.error("no error will happen"));
  console.log("finish async");
}

async function main() {
  console.log("before foo()");
  await foo();
  console.log("after foo()");
}

main();

Result:

before foo()
start async
do something with result: helloworld
finish async
after foo()

vs second code block:

const someAsyncOperation = ms => new Promise(res => setTimeout(res, ms, "hello"))

async function foo(){
  console.log("start async");
  someAsyncOperation(1500)
    .then(res => console.log("do something with result:", res + "world"))
    .catch(() => console.error("no error will happen"));
  console.log("finish async");
}

async function main() {
  console.log("before foo()");
  await foo();
  console.log("after foo()");
}

main();

Result:

before foo()
start async
finish async
after foo()
do something with result: helloworld

As you can see, the sequence of actions is different in both cases.

  1. In the first case, an await will make the entirety of foo() finish before continuing.
  2. In the second, there is no await, so someAsyncOperation is a fire and forget. Your code will finish executing before it does, so you'll never be notified for success or failures.

Also, I have to note that this is only if you call the functions with await. If you don't, then the code will never wait for it to finish in either case.

const someAsyncOperation = ms => new Promise(res => setTimeout(res, ms, "hello"))

async function foo(){
  console.log("start async");
  await someAsyncOperation(1500)
    .then(res => console.log("do something with result:", res + "world"))
    .catch(() => console.error("no error will happen"));
  console.log("finish async");
}

async function main() {
  console.log("before foo()");
  foo(); //no await
  console.log("after foo()");
}

main();

const someAsyncOperation = ms => new Promise(res => setTimeout(res, ms, "hello"))

async function foo(){
  console.log("start async");
  someAsyncOperation(1500)
    .then(res => console.log("do something with result:", res + "world"))
    .catch(() => console.error("no error will happen"));
  console.log("finish async");
}

async function main() {
  console.log("before foo()");
  foo(); //no await
  console.log("after foo()");
}

main();

The two operations are essentially the same. There is a difference where "finish async" shows up but only because I added it for clarity of how the function is handled. In your code, you don't have anything else after the promise is fired off, so there is not going to be a difference. In both cases foo() itself is a fire and forget, since it's not awaited. Therefore, it doesn't matter if you await the operation inside or not.

At any rate, there isn't exactly a generically "better" way to use promises out of these.

Sometimes you may want a fire and forget functionality so that you don't really wait. As a simple example:

showSpinner();
getData()
    .then(data => {
        hideSpinner();
        showData(data);
    })
    .catch(error => {
        hideSpinner();
    }

/* do more stuff */

Presumably, it's some sort of non-crucial data - we can show it or not but we want to remove the spinner.

Other times, you may actually want to wait and verify an operation succeeds or not before continuing. For example something like:

try {
    const user = await registerUser(userInfo);
    await processPayment(user);
} catch (error) {
    cancelTransaction();
    throw new RegistrationAndPaymentError(error);
}

/* do more stuff */

If the registration fails, we have to pump the breaks and avoid continuing with the process.

Which one you choose depends on how you want to handle a given operation. Some you don't really care when they complete and how, others may prevent further operations.

Also worth clarifying that every async/await usage can be changed to the promise API by chaining .then() and .catch(). However, sometimes chaining a lot of promise operations is not as readable as using awaits. Most of the promise API operations can also be expressed using async/await. So, which one you choose is often based on which one you prefer. It's generally advisible not to mix the two types of syntax - nothing would go wrong if you do but it's clearer if you stick to one or the other.

With all that said, it's also advisable to make your functions with asynchronous operations awaitable. The reason is that perhaps right now you may not want to wait for them to finish but in the future you might.

With your first bit of code await handleChangeSelect() will already force the execution to pause until the function is complete so it's basically OK as it is. Granted, it would be better if it didn't mix await with .then() and .catch() but it's still not wrong.

The way to make it possible to react to a function finishing without adding an await in it (essentially, by using only the promise API), you need to return the promise that the inner function produces. So you can change it to:

function handleChangeSelect(value,action){
   return getOneOrg(value.label).then(res=>{
        const updatedCategory = {...modal, [action.name]:value, categories:[{value:res.ans,label:res.ans}]}
        setModal(updatedCategory)
    }).catch(err=>console.log(err))
}

This will make it possible to react to the function completion:

const someAsyncOperation = ms => new Promise(res => setTimeout(res, ms, "hello"))

async function foo(){
  console.log("start async");
  return someAsyncOperation(1500)
    .then(res => console.log("do something with result:", res + "world"))
    .catch(() => console.error("no error will happen"))
    .then(() => console.log("finish async")); //we don't want any code after the async call
                                              //so, further code will be chained as .then()
}

async function main() {
  console.log("before foo()");
  await foo();
  console.log("after foo()");
}

main();

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.