This week’s intensive reading is Escape async/await Hell.

1 the introduction

Finally async/await is being teased too. Aditya Agarwal thinks async/await syntax gets us into new trouble.

In fact, I have long thought something is wrong, finally someone said the truth, async/await might cause trouble.

2 an overview

Here’s the modern front-end code you can see everywhere:

(async() = > {const pizzaData = await getPizzaData(); // async call
  const drinkData = await getDrinkData(); // async call
  const chosenPizza = choosePizza(); // sync call
  const chosenDrink = chooseDrink(); // sync call
  await addPizzaToCart(chosenPizza); // async call
  await addDrinkToCart(chosenDrink); // async call
  orderItems(); // async call}) ();Copy the code

There is nothing wrong with the await syntax, sometimes the user is using it incorrectly. When there is no dependency between pizzaData and drinkData, the sequential await will at most double the execution time of the getPizzaData function because getPizzaData and getDrinkData should be executed in parallel.

Back to our joke about callback hell, having at least two lines of callback code does not block, even though the code is ugly.

It seems that the simplification of syntax brings performance problems and directly affects the user experience. Is it worth reflecting on?

The correct way to execute asynchronous functions in parallel is to wait and await the return value:

(async() = > {const pizzaPromise = selectPizza();
  const drinkPromise = selectDrink();
  await pizzaPromise;
  await drinkPromise;
  orderItems(); // async call}) ();Copy the code

Or use promise.all to make the code more readable:

(async() = > {Promise.all([selectPizza(), selectDrink()]).then(orderItems); // async call}) ();Copy the code

Don’t await it, it may degrade your code performance.

3 intensive reading

Think carefully about why async/await is abused. I think it is because of its counterintuitive function.

First async/await is really syntactic sugar and only makes code more comfortable to write. Regardless of its grammar or characteristics, only from the grammar sugar three words, we can see that it must be limited to some abilities.

For example, we use HTML tags to encapsulate a component, bringing convenience while ensuring that its functionality is a subset of HTML. For example, if a component API is too complex for some wheels, and it encapsulates a syntactic sugar, we can assume that some functionality is being sacrificed for this convenience.

Functional integrity and ease of use have always been a game, and many open source versions of framework ideas are almost always the result of mixing functional integrity and convenience in different proportions.

So back to async/await it solves the problem that callback hell brings disaster:

a((a)= > {
  b((a)= > {
    c();
  });
});
Copy the code

To reduce the impact of too many nested structures on the brain, async/await decides to write:

await a();
await b();
await c();
Copy the code

Hierarchically consistent, but logically nested, isn’t that another level of brain overload? And because this transformation is invisible, we tend to ignore it a lot of the time, which leads to grammatical sugar abuse.

Understanding grammar sugar

While it may be anti-human to understand the true effects of async/await, it is important to understand the changes async/await brings in order to keep code structure clean and prevent writing low-performing code.

First of all async/await only implements part of the functionality supported by callbacks, that is, it is only convenient for dealing with layers of nested scenarios. For other scenes, you need to think about it.

For example, two pairs of callbacks:

a((a)= > {
  b();
});

c((a)= > {
  d();
});
Copy the code

The following is the least efficient method of execution, although it ensures consistent functionality:

await a();
await b();
await c();
await d();
Copy the code

Because when translated as a callback, it becomes:

a((a)= > {
  b((a)= > {
    c((a)= > {
      d();
    });
  });
});
Copy the code

However, we find that in the original code, function C can be executed at the same time as a, but the async/await syntax makes us prefer to execute C after B.

So when we realize this, we can optimize the performance:

const resA = a();
const resC = c();

await resA;
b();
await resC;
d();
Copy the code

In fact, this logic can not achieve the effect of the callback, although a and C are executed at the same time, but D only needs to wait for C to complete, now if A is executed longer than C, it will become:

a((a)= > {
  d();
});
Copy the code

It seems that only two functions are completely separated:

(async() = > {awaita(); b(); }) ();(async () = > {await c();
  d();
})(a);
Copy the code

Or use Promise. All:

async function ab() {
  await a();
  b();
}

async function cd() {
  await c();
  d();
}

Promise.all([ab(), cd()]);
Copy the code

That’s what I’m trying to say about the horror. It is worse than callback hell to write simple procedural code with async/await and then to reflect on it and push back to optimize performance.

And most of the scene code is very complex, synchronization and await mixed together, trying to understand the context and optimize the performance is often very difficult. But why do we dig and fill our own holes? A lot of times it can cause forgetting.

The author gives the promise.all approach to simplify the logic, but I believe that instead of just going for async/await syntax, the appropriate use of callbacks when necessary can increase the readability of the code.

4 summarizes

Async /await callback hell reminds us not to rely too much on new features, which can lead to a decrease in the efficiency of code execution and thus affect the user experience. At the same time, I don’t think you should over-use new features to fix the problems caused by new features, which will lead to code readability.

When I opened the story just fire up the period of the old code, saw many transition abstraction, for use with the code, just write two lines of code to the logic, split into three files, scattered in different locations six lines, so I had to use a string search way to find clues, finally found the whole project in just a abstract code.

There is only one possibility of writing such code, which is to drink all of Redux’s chicken soup in one sitting while mentally numb.

Like async/await hell, seeing this redux code makes me feel inferior to jquery code written by an old front end that is not up to date.

It is the thinking that determines the quality of your code, not the framework or syntax. Async /await is good, but moderate.

5 More Discussions

Close reading Escape async/await Hell · Issue #82 · dT-fe /weekly

If you’d like to participate in the discussion, pleaseClick here to, with a new theme every week, released on weekends or Mondays.