What's wrong with awaiting a promise chain?
Asked Answered
A

3

17

I’m working on an Angular 6 application and I’ve been told the following is an anti-pattern:

await someFunction().then(result => {
    console.log(result);
});

I realize that it is pointless to await a promise chain. If someFunction() returns a promise, you don’t need a promise chain if you’re awaiting it. You can do this:

const result = await someFunction();
console.log(result);

But I’m being told awaiting a promise chain can cause bugs, or that it will break things in my code. If the first code snippet above does the same thing as the second snippet, what does it matter which one is used. What dangers does the first snippet introduce that the second one doesn’t?

Anhedral answered 27/1, 2019 at 6:37 Comment(1)
I think a top-level "await someFunction()" is not possible, it has to be wrapped inside some async function, or not?Selfmade
S
13

Under the hood, async/await is just promises.

That is, when you have some code that looks like:

const result = await myAsyncFunction();   
console.log(result): 

That's exactly the same as writing:

myAsyncFunction().then(data => {
   const result = data; 
   console.log(result); 
}); 

The reason then - that you shouldn't mix async/await and .then chains - is because it's confusing.

It's better to just pick one style, and stick to it.

And while you're picking one - you might as well pick async/await - it's more understandable.

Scampi answered 27/1, 2019 at 6:50 Comment(2)
it's more understandable that's 100% opinion. I've found it difficult to explain to some people that an async function actually returns to the caller at the first await expression. Promises alone have less of a learning curve than async / await, in my opinion.Truax
actually, I think that first code example above is wrong, because there is no top-level await call.. It should be wrapped inside some async function() block, otherwise the awai can not be executed, or am I wrong? Thats the disadvantage of async-await in my opinion... its a bit overhead imho..Selfmade
T
16

I’m being told awaiting a promise chain will break things in my code.

Not necessarily, your two code snippets do indeed work the same (as long as someFunction() really returns a promise).

What does it matter which one is used. What dangers does the first snippet introduce that the second one doesn’t?

It's harder to understand and maintain, it's confusing to mix different styles. Confusion leads to bugs.

Consider that you would need to add another promise call at the location of the console.log() call, or even a conditional return from the function. Can you use await in the callback like elsewhere in the function, do you need to return the result from the then callback, is it even possible to return from the outer function? All these questions don't even come up in the first snippet. And while they can be easily answered for your toy example, it might not be as easy in real code with more complex and nested control flow.

So you should prefer the more concise and clean one. Stick to await for consistency, avoid then in async functions1.

1: Of course, there's always an exception to the rule. I would say that it's cleaner to use promise chaining for error handling in some cases where you would use catch or the second then callback.

Theomorphic answered 27/1, 2019 at 12:2 Comment(0)
S
13

Under the hood, async/await is just promises.

That is, when you have some code that looks like:

const result = await myAsyncFunction();   
console.log(result): 

That's exactly the same as writing:

myAsyncFunction().then(data => {
   const result = data; 
   console.log(result); 
}); 

The reason then - that you shouldn't mix async/await and .then chains - is because it's confusing.

It's better to just pick one style, and stick to it.

And while you're picking one - you might as well pick async/await - it's more understandable.

Scampi answered 27/1, 2019 at 6:50 Comment(2)
it's more understandable that's 100% opinion. I've found it difficult to explain to some people that an async function actually returns to the caller at the first await expression. Promises alone have less of a learning curve than async / await, in my opinion.Truax
actually, I think that first code example above is wrong, because there is no top-level await call.. It should be wrapped inside some async function() block, otherwise the awai can not be executed, or am I wrong? Thats the disadvantage of async-await in my opinion... its a bit overhead imho..Selfmade
D
-1

If your then code returned a promise instead of calling console.log, your first example would await, but your second example would not.

When you use async/await, you will catch your rejects in try/catch blocks. Your code will be less nested and clearer.

Using then often results in more nesting, and harder to read code.

You can await anything, whether or not it returns a promise. Sometimes this future-proofs calling a method that may one day become async or just return a promise without declaring async.

The downsides are complexity, performance, and compatibility, all of which pale in comparison to the gains.

I find that if you rely on a function's return value after calling it, and it is or may eventually become asynchronous, decorate calling your functions with await to your heart's delight, whether or not it is current async or returns a promise.

Denysedenzil answered 27/1, 2019 at 6:53 Comment(8)
For explaining a scenario where this introduces a bug, +1. For concluding with bad (can't tell if sarcastic?) advice, -1.Truax
I don't see how any of this is sarcastic. Are you one of those async/await haters? I have taught hundreds of student to use them, and then NEVER go back.Denysedenzil
So decorate your code with await to your heart's delight! seems very much like it's implying to use it when it's unnecessary, especially when coupled with your previous assertion that you can await anything. So, should I var foo = await "bar"; just because it's valid? Maybe someday I'll change that string to a promise? Meanwhile I have to put it in an async function just because I felt like sticking a random await expression to "future-proof" something. Yes I'm being ridiculous, but it still sounds like sarcasm to me when I read the statement at the end of your answer.Truax
I suggest you look up the definition or sarcasm. In the meantime I will clarify to await calling functions, although that is really self explanatory. If you call foo() and it is not async, but may be some day, there is NO HARM to awaiting it.Denysedenzil
Yes there is. If the function returns an object that defines a then() function, awaiting it is completely different than returning it. That is indeed harmful, and not compatible behavior.Truax
Yor last point was the first line stated in my solution, and exactly why using await generously has an advantage.Denysedenzil
"foo() is sync but may some day become async" - that's no good reason to use await, treating it as if it was async today already. Whether a function is asynchronous or not is an important part of its API contract, and must not change offhandedly. Document what you are doing and keep it at that.Theomorphic
@bergi I disagree 100% and am going to leave it at that.Denysedenzil

© 2022 - 2024 — McMap. All rights reserved.