2

I'm reading this article from V8 about how ES2017 introduced an optimization for async/await to reduce the number of Promises needed in runtime execution. Essentially avoiding an extra Promise when the value being awaited is already a Promise. This also enabled efficient async stack trace to be collected in Errors.

In the article it gave a high-level explanation of how async function translate into internal operations. And here is a figure they used in question:

I think the author is using ECMAScript terms for naming the internal operations.

What I don't understand though is the throwaway promise. It's passed to the performPromiseThen operation. However the signature for PerformPromiseThen is:

PerformPromiseThen( promise, onFulfilled, onRejected [ , resultCapability ] )

The last optional argument being a PromiseCapability Record. I also can't quite grasp the idea of this PromiseCapability Record. It seems to be used in async iterators but not really sure how. It doesn't seem to be the same thing throwaway promise was representing. So my question is what exactly is this throwaway promise, and what is the semantics of this parameter passed to performPromiseThen?

I can rewrite their internal operations back into plain Promise based on my understanding, but the throwaway promise is still a missing piece that I cannot have a mental mapping to things I can understand.

function foo(v) {                                 // resumable function foo(v) {
  const implicit_promise = new Promise            //   implicit_promise = createPromise();
  ((resolvePromise, _throw) => {
                                                  //   // 1. wrap v in a promise
    const promise = new Promise(resolvePromise => //   promise = createPromise();
      resolvePromise(/* promise, */ v));          //   resolvePromise(promise, v);

                                                  //   // 2. attach handlers for resuming foo
    /* throwaway = createPromise(); */            //   throwaway = createPromise();
    promise.then(                                 //   performPromiseThen(promise,
      res => resume(/* «foo», */ res),            //     res => resume(«foo», res),
      err => _throw(/* «foo», */ err),            //     err => throw(«foo», err),
      /* throwaway */);                           //     throwaway);

    function resume(/* «foo», */ res) {           //   // 3.a suspend foo ...
      w = /* resumed */ res;                      //   w = suspend(«foo», ...);
      resolvePromise(/* implicit_promise, */ w);  //   resolvePromise(implicit_promise, w);
    }
  });                                             //   // 3.b ... and return implicit_promise
  return implicit_promise;                        //   suspend(..., implicit_promise);
}      
Rix
  • 1,688
  • 13
  • 20

1 Answers1

2

I also can't quite grasp the idea of this PromiseCapability Record.

In the spec, it's an internal value "used to encapsulate a Promise […] along with the functions that are capable of resolving or rejecting that promise.", basically implementing the deferred pattern.

It doesn't seem to be the same thing throwaway promise was representing.

Not quite the same, but representing the same thing. In V8, (a reference to) the promise object is all you need to resolve it, just call some of its internal methods. So the code does just directly pass the promise that PerformPromiseThen should resolve.

What exactly is this throwaway promise, and what is the semantics of this parameter passed to performPromiseThen?

A .then() call deals with two separate promises: in b = a.then(), a is the promise that the handlers are installed on where as b is a new promise created by the method to be resolved with the result of the handlers. This second promise is what the resultCapability is used for.

In other words, you're looking for

const throwaway = promise.then(
    res => _resume(/* «foo», */ res),
    err => _throw(/* «foo», */ err),
);

where the throwaway value is not used afterwards but just thrown away. (Notice it always will fulfill anyway, so no need for unhandled rejection tracking).

However the signature for PerformPromiseThen is:

PerformPromiseThen( promise, onFulfilled, onRejected [ , resultCapability ] )

The last argument being optional

Indeed, however at the time the article you're quoting was written, the argument was not yet optional.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Thanks, it makes more sense! So I can write it like this? https://pastebin.com/gWYNjXYu – Rix May 31 '23 at 21:31
  • Probably without the `/* promise */` and `, /* throwaway */`, but it's all just pseudocode with unclear semantics anyway – Bergi May 31 '23 at 21:38