JS Asynchronous Stack Tracing: Why await is better than Promise

JS Asynchronous Stack Tracing: Why await is better than Promise

Overview

The fundamental difference between async/await and Promise is that await fn() suspends the execution of the current function, while promise.then(fn) continues the execution of the current function after adding the fn call to the callback chain.

const fn = () => console.log('hello')
const a = async () => {
  await fn() // Pause execution of fn }
// When a is called, the execution of fn is resumed a() // "hello"

const promise = Promise.resolve()
// After adding fn to the callback chain, continue executing fn
promise.then(fn) // "hello"

In the context of a stack trace, this difference is very significant.

When a Promise chain (whether desugared or not) throws an unhandled exception at any time, the JavaScript engine will display an error message and (hopefully) log a useful stack trace.

As a developer, you'll expect this whether you're using normal Promises or async await.

Promises

Imagine a scenario where when the call to async function b resolves, function c is called:

const b = () => Promise.resolve()
const a = () => {
    b().then(() => c())
}

When a is called, the following happens synchronously:

  • b is called and returns a Promise that will be resolved at some point in the future.
  • The .then callback (which actually calls c() ) is added to the callback chain (in V8 terms, [...] is added as a resolve handler).

After that, we finish executing the code in the body of function a. A is never suspended, and by the time the async call to B resolves, the context is gone.

Imagine what would happen if b (or c) threw an exception asynchronously? Ideally, the stack trace should include A, since B (or C) was called from there, right? How can we do this now that we are no longer referring to a?

In order for this to work, the JavaScript engine needs to do something beyond the steps above: it captures and stores the stack trace whenever it has the chance.

In V8, the stack trace is attached to the Promise returned by b. When the Promise fulfills, the stack trace will be passed along so that c can use it as needed.

b()[a] -> b().then()[a] -> c[a?:a]

Capturing stack traces takes time (i.e., slows performance); storing these stack traces requires memory.

async/await

Here is the same program, written using async/await instead of Promises:

const b = () => Promise.resolve()
const a = async () => {
  await b()
  c()
}

Using await, we can resume the call chain even if the stack trace is not collected in the await call.

This is possible because A is suspended, waiting for B to resolve. If b throws an exception, the stack trace can be reconstructed in this way on demand.

If c throws an exception, the stack trace can be constructed just like for a synchronous function, because we are still in the context of a when this happens.

Enable JavaScript engines to process stack traces in a more efficient way by following these recommendations:

  • Prefer async/await over Promises.
  • Use @babel/preset env to avoid unnecessary async/await transfers.

The above is the details of JS asynchronous stack tracing and why await is better than Promise. For more information about Javascript, please pay attention to other related articles on 123WORDPRESS.COM!

You may also be interested in:
  • How to implement asynchronous calls with async/await in JS
  • NodeJs handles asynchronous methods through async/await
  • async/await and promise (asynchronous operation problem in nodejs)
  • Can asynchrony in JavaScript save await?

<<:  Tutorial on downloading, installing, configuring and using MySQL under Windows

>>:  Solve the problem of wireless and audio not working after Windows Server installation

Recommend

The whole process of upgrading Angular single project to multiple projects

Table of contents Preface Development Environment...

This article will help you understand the life cycle in Vue

Table of contents 1. beforeCreate & created 2...

Detailed explanation of Vue's ref attribute

Summarize This article ends here. I hope it can b...

The complete version of the common Linux tool vi/vim

Why learn vim Linux has a large number of configu...

Develop calculator example code using native javascript

The main function of a calculator is to perform n...

WeChat applet implements a simple dice game

This article shares the specific code of the WeCh...

How to use VLAN tagged Ethernet card in CentOS/RHEL system

In some scenarios, we want to assign multiple IPs...

Summary of problems encountered when installing docker on win10 home version

Docker download address: http://get.daocloud.io/#...

Import csv file into mysql using navicat

This article shares the specific code for importi...

Implementation and optimization of MySql subquery IN

Table of contents Why is IN slow? Which is faster...

MySQL multi-instance configuration application scenario

Table of contents MySQL multiple instances Multi-...

js canvas realizes rounded corners picture

This article shares the specific code of js canva...

A brief analysis of the matching priority of Nginx configuration location

Preface The location in the server block in the N...

Analysis of the reasons why Vue3 uses Proxy to implement data monitoring

Vue data two-way binding principle, but this meth...