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

Understanding the CSS transform-origin property

Preface I recently made a fireworks animation, wh...

Detailed steps to install xml extension in php under linux

Installing XML extension in PHP Linux 1. Enter th...

JavaScript to implement the countdown for sending SMS

This article shares the specific code of JavaScri...

The process of building lamp architecture through docker container

Table of contents 1. Pull the centos image 2. Bui...

Vue monitoring properties and calculated properties

Table of contents 1. watch monitoring properties ...

How MLSQL Stack makes stream debugging easier

Preface A classmate is investigating MLSQL Stack&...

Small paging design

Let our users choose whether to move forward or ba...

Introduction to MySQL isolation level, lock and MVCC

This article aims to clarify the relationship bet...

Robots.txt detailed introduction

Basic introduction to robots.txt Robots.txt is a p...

Use of Docker UI, a Docker visualization management tool

1. Introduction to DockerUI DockerUI is based on ...

Detailed example of inserting custom HTML records in Quill editor

It is already 2020. Hungry humans are no longer s...

How to insert weather forecast into your website

We hope to insert the weather forecast into the w...

Detailed explanation of MySQL execution plan

The EXPLAIN statement provides information about ...

HTML implements read-only text box and cannot modify the content

Without further ado, I will post the code for you...