Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add initial support for promises #1923

Closed
wants to merge 16 commits into from

Conversation

aaronmunsters
Copy link
Contributor

It adds the following:

An example program that shows the control flow with this addition is:

new Promise((res, rej) => {
  console.log("A");
  res(undefined);
}).then((_) => console.log("B"));
console.log("C");

Which would output:

A
C
B

Feedback is most welcome! 😉

@codecov
Copy link

codecov bot commented Mar 13, 2022

Codecov Report

Merging #1923 (7d6f902) into main (09bfabb) will decrease coverage by 0.07%.
The diff coverage is 52.17%.

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1923      +/-   ##
==========================================
- Coverage   45.98%   45.90%   -0.08%     
==========================================
  Files         206      209       +3     
  Lines       17057    17317     +260     
==========================================
+ Hits         7844     7950     +106     
- Misses       9213     9367     +154     
Impacted Files Coverage Δ
boa_engine/src/builtins/mod.rs 7.93% <0.00%> (-0.13%) ⬇️
boa_engine/src/lib.rs 79.31% <ø> (ø)
boa_engine/src/object/mod.rs 20.98% <9.09%> (-0.38%) ⬇️
boa_engine/src/builtins/promise/promise_job.rs 34.48% <34.48%> (ø)
boa_engine/src/context/intrinsics.rs 58.46% <50.00%> (+0.98%) ⬆️
boa_engine/src/builtins/promise/mod.rs 60.90% <60.90%> (ø)
boa_engine/src/context/mod.rs 33.48% <62.50%> (+0.78%) ⬆️
boa_engine/src/job.rs 81.81% <81.81%> (ø)
boa_engine/src/builtins/array/mod.rs 74.59% <0.00%> (-5.65%) ⬇️
boa_engine/src/builtins/array/array_iterator.rs 83.33% <0.00%> (-3.34%) ⬇️
... and 30 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 09bfabb...7d6f902. Read the comment docs.

@Razican Razican added this to the v0.15.0 milestone Mar 13, 2022
@Razican Razican added builtins PRs and Issues related to builtins/intrinsics enhancement New feature or request labels Mar 13, 2022
Copy link
Member

@jedel1043 jedel1043 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good work! I really like the idea of having a simple queue executor until we make the VM async.
This is a superficial review, I'll do a proper review later when I'm free :)

boa_engine/src/job.rs Show resolved Hide resolved
boa_engine/src/context/mod.rs Show resolved Hide resolved
@Razican
Copy link
Member

Razican commented Mar 16, 2022

VM implementation

Test result main count PR count difference
Total 88,410 88,410 0
Passed 43,978 44,104 +126
Ignored 21,481 21,481 0
Failed 22,951 22,825 -126
Panics 0 2 +2
Conformance 49.74% 49.89% +0.14%
Fixed tests (126):
test/built-ins/Object/seal/seal-promise.js [strict mode] (previously Failed)
test/built-ins/Object/seal/seal-promise.js (previously Failed)
test/built-ins/Promise/resolve-function-length.js [strict mode] (previously Failed)
test/built-ins/Promise/resolve-function-length.js (previously Failed)
test/built-ins/Promise/constructor.js [strict mode] (previously Failed)
test/built-ins/Promise/constructor.js (previously Failed)
test/built-ins/Promise/resolve-function-extensible.js [strict mode] (previously Failed)
test/built-ins/Promise/resolve-function-extensible.js (previously Failed)
test/built-ins/Promise/executor-not-callable.js [strict mode] (previously Failed)
test/built-ins/Promise/executor-not-callable.js (previously Failed)
test/built-ins/Promise/reject-function-prototype.js [strict mode] (previously Failed)
test/built-ins/Promise/reject-function-prototype.js (previously Failed)
test/built-ins/Promise/get-prototype-abrupt.js [strict mode] (previously Failed)
test/built-ins/Promise/get-prototype-abrupt.js (previously Failed)
test/built-ins/Promise/reject-function-length.js [strict mode] (previously Failed)
test/built-ins/Promise/reject-function-length.js (previously Failed)
test/built-ins/Promise/get-prototype-abrupt-executor-not-callable.js [strict mode] (previously Failed)
test/built-ins/Promise/get-prototype-abrupt-executor-not-callable.js (previously Failed)
test/built-ins/Promise/resolve-function-nonconstructor.js [strict mode] (previously Failed)
test/built-ins/Promise/resolve-function-nonconstructor.js (previously Failed)
test/built-ins/Promise/executor-call-context-sloppy.js (previously Failed)
test/built-ins/Promise/name.js [strict mode] (previously Failed)
test/built-ins/Promise/name.js (previously Failed)
test/built-ins/Promise/resolve-function-property-order.js [strict mode] (previously Failed)
test/built-ins/Promise/resolve-function-property-order.js (previously Failed)
test/built-ins/Promise/is-a-constructor.js [strict mode] (previously Failed)
test/built-ins/Promise/is-a-constructor.js (previously Failed)
test/built-ins/Promise/undefined-newtarget.js [strict mode] (previously Failed)
test/built-ins/Promise/undefined-newtarget.js (previously Failed)
test/built-ins/Promise/property-order.js [strict mode] (previously Failed)
test/built-ins/Promise/property-order.js (previously Failed)
test/built-ins/Promise/reject-function-property-order.js [strict mode] (previously Failed)
test/built-ins/Promise/all/S25.4.4.1_A4.1_T1.js [strict mode] (previously Failed)
test/built-ins/Promise/all/S25.4.4.1_A4.1_T1.js (previously Failed)
test/built-ins/Promise/all/ctx-non-object.js [strict mode] (previously Failed)
test/built-ins/Promise/all/ctx-non-object.js (previously Failed)
test/built-ins/Promise/all/not-a-constructor.js [strict mode] (previously Failed)
test/built-ins/Promise/all/not-a-constructor.js (previously Failed)
test/built-ins/Promise/reject/ctx-non-ctor.js [strict mode] (previously Failed)
test/built-ins/Promise/reject/ctx-non-ctor.js (previously Failed)
test/built-ins/Promise/reject/ctx-non-object.js [strict mode] (previously Failed)
test/built-ins/Promise/reject/ctx-non-object.js (previously Failed)
test/built-ins/Promise/reject/not-a-constructor.js [strict mode] (previously Failed)
test/built-ins/Promise/reject/not-a-constructor.js (previously Failed)
test/built-ins/Promise/reject/S25.4.4.4_A3.1_T1.js [strict mode] (previously Failed)
test/built-ins/Promise/reject/S25.4.4.4_A3.1_T1.js (previously Failed)
test/built-ins/Promise/prototype/S25.4.5_A3.1_T1.js [strict mode] (previously Failed)
test/built-ins/Promise/prototype/S25.4.5_A3.1_T1.js (previously Failed)
test/built-ins/Promise/prototype/prop-desc.js [strict mode] (previously Failed)
test/built-ins/Promise/prototype/prop-desc.js (previously Failed)
test/built-ins/Promise/prototype/S25.4.4.2_A1.1_T1.js [strict mode] (previously Failed)
test/built-ins/Promise/prototype/S25.4.4.2_A1.1_T1.js (previously Failed)
test/built-ins/Promise/prototype/proto.js [strict mode] (previously Failed)
test/built-ins/Promise/prototype/proto.js (previously Failed)
test/built-ins/Promise/prototype/then/S25.4.5.3_A2.1_T2.js [strict mode] (previously Failed)
test/built-ins/Promise/prototype/then/S25.4.5.3_A2.1_T2.js (previously Failed)
test/built-ins/Promise/prototype/then/prop-desc.js [strict mode] (previously Failed)
test/built-ins/Promise/prototype/then/prop-desc.js (previously Failed)
test/built-ins/Promise/prototype/then/not-a-constructor.js [strict mode] (previously Failed)
test/built-ins/Promise/prototype/then/not-a-constructor.js (previously Failed)
test/built-ins/Promise/prototype/then/name.js [strict mode] (previously Failed)
test/built-ins/Promise/prototype/then/name.js (previously Failed)
test/built-ins/Promise/prototype/then/ctor-null.js [strict mode] (previously Failed)
test/built-ins/Promise/prototype/then/ctor-null.js (previously Failed)
test/built-ins/Promise/prototype/then/S25.4.5.3_A2.1_T1.js [strict mode] (previously Failed)
test/built-ins/Promise/prototype/then/S25.4.5.3_A2.1_T1.js (previously Failed)
test/built-ins/Promise/prototype/then/S25.4.5.3_A1.1_T1.js [strict mode] (previously Failed)
test/built-ins/Promise/prototype/then/S25.4.5.3_A1.1_T1.js (previously Failed)
test/built-ins/Promise/prototype/finally/not-a-constructor.js [strict mode] (previously Failed)
test/built-ins/Promise/prototype/finally/not-a-constructor.js (previously Failed)
test/built-ins/Promise/prototype/catch/not-a-constructor.js [strict mode] (previously Failed)
test/built-ins/Promise/prototype/catch/not-a-constructor.js (previously Failed)
test/built-ins/Promise/prototype/catch/this-value-non-object.js [strict mode] (previously Failed)
test/built-ins/Promise/prototype/catch/this-value-non-object.js (previously Failed)
test/built-ins/Promise/prototype/catch/this-value-then-not-callable.js [strict mode] (previously Failed)
test/built-ins/Promise/prototype/catch/this-value-then-not-callable.js (previously Failed)
New panics (2):
test/built-ins/Promise/prototype/no-promise-state.js [strict mode] (previously Failed)
test/built-ins/Promise/prototype/no-promise-state.js (previously Failed)

@Razican
Copy link
Member

Razican commented Mar 16, 2022

I really like how this looks! It's a great step in the right direction. Maybe you can already remove the ignored flag:async tests from the test_ignore.txtI still have to review this properly (even though most of those require extra tester support).

In any case, there is something that has already caught my attention. The usage of the queues crate. This crate was last updated in 2018 (more than 3 years ago), so I'm not comfortable adding it as a dependency. In fact, I might even prefer to create a "boa_queues" crate or something and adapt it. The crate itself is only 1 Rust file (we need to check licensing, though).

Copy link
Member

@Razican Razican left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, I got the time to review it a bit more, and I noticed it's missing some documentation. The rest looks pretty good!

Also, we must check where are those panics coming from and fix them.

Comment on lines +193 to +206
struct ResolvedRecord {
value: bool,
}

struct ResolvingFunctionsRecord {
resolve: JsValue,
reject: JsValue,
}

#[derive(Debug, Trace, Finalize)]
struct RejectResolveCaptures {
promise: JsObject,
already_resolved: JsObject,
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add some documentation to these structures? Also, they must all implement Debug, Clone and Copy, when possible

boa_engine/src/builtins/promise/mod.rs Show resolved Hide resolved
Comment on lines +35 to +74
#[derive(Debug, Clone, Trace, Finalize)]
enum PromiseState {
Pending,
Fulfilled,
Rejected,
}

#[derive(Debug, Clone, Trace, Finalize)]
pub struct Promise {
promise_result: Option<JsValue>,
promise_state: PromiseState,
promise_fulfill_reactions: Vec<ReactionRecord>,
promise_reject_reactions: Vec<ReactionRecord>,
promise_is_handled: bool,
}

#[derive(Debug, Clone, Trace, Finalize)]
pub struct ReactionRecord {
promise_capability: Option<PromiseCapability>,
reaction_type: ReactionType,
handler: Option<JobCallback>,
}

#[derive(Debug, Clone, Trace, Finalize)]
enum ReactionType {
Fulfill,
Reject,
}

#[derive(Debug, Clone, Trace, Finalize)]
struct PromiseCapability {
promise: JsValue,
resolve: JsValue,
reject: JsValue,
}

#[derive(Debug, Trace, Finalize)]
struct PromiseCapabilityCaptures {
promise_capability: Gc<boa_gc::Cell<PromiseCapability>>,
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to have some documentation for these structures, to understand their purpose, if there is any reference to them in the spec, and ReactionType and PromiseState can probably implement Copy, which would make things easier.

For Copy types, you will probably need to implement Trace and Finalize separately, with an empty implementation, or better: whenever a structure requires them in, use the #[unsafe_ignore_trace] attribute on the field.

@@ -0,0 +1,731 @@
//! This module implements the global `Promise` object.

#![allow(dead_code, unused_results, unused_variables)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this shouldn't stay here, right? We don't want to silence warnings :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're absolutely right, forgot to remove this :)


use super::{Promise, PromiseCapability};

pub(crate) struct PromiseJob;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add some documentation here, add the reference to the spec, and derive Debug, Clone and Copy for it?

Comment on lines +14 to +18
#[derive(Debug, Trace, Finalize)]
struct ReactionJobCaptures {
reaction: ReactionRecord,
argument: JsValue,
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would need some documentation to explain where does it come from, what it's used for, how...

Comment on lines +179 to +194
#[derive(Debug, Trace, Finalize)]
struct JobCapture {
promise_to_resolve: JsObject,
thenable: JsValue,
then: JobCallback,
}

impl JobCapture {
fn new(promise_to_resolve: JsObject, thenable: JsValue, then: JobCallback) -> Self {
Self {
promise_to_resolve,
thenable,
then,
}
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would need some documentation to know where it's coming from, how to use it, what it means and so on. Maybe even a spec link. Also for the new() function.

Comment on lines +5 to +6
#[derive(Debug, Clone, Trace, Finalize)]
pub struct JobCallback {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to have a bit of documentation in this structure, to understand it better.

Comment on lines +11 to +38
pub fn make_job_callback(callback: JsValue) -> Self {
Self {
callback: Box::new(callback),
}
}

pub fn call_job_callback(
&self,
v: &JsValue,
argument_list: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let callback = match *self.callback {
JsValue::Object(ref object) if object.is_callable() => object.clone(),
_ => panic!("Callback is not a callable object"),
};

callback.__call__(v, argument_list, context)
}

pub fn run(&self, context: &mut Context) {
let callback = match *self.callback {
JsValue::Object(ref object) if object.is_callable() => object.clone(),
_ => panic!("Callback is not a callable object"),
};

let _callback_result = callback.__call__(&JsValue::Undefined, &[], context);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add documentation to these functions?

@lastmjs
Copy link
Contributor

lastmjs commented Mar 18, 2022

Does this help us get toward allowing https://boa-dev.github.io/boa/doc/boa_engine/builtins/function/type.NativeFunctionSignature.html to be asynchronous?

@aaronmunsters
Copy link
Contributor Author

Does this help us get toward allowing https://boa-dev.github.io/boa/doc/boa_engine/builtins/function/type.NativeFunctionSignature.html to be asynchronous?

This contribution does not add an executor/async rust code, all code that you would add as native functions will be executed on the single/main rust thread that executes all JS code in order of the jobqueue after executing the main program (run-to-completion).

Say that you would now add fetch to Boa which would add a promise that represents the completion of a network request.
If you would now write a program that contains two fetch expressions (each including a .then(logResult) statement), then as soon as the main JS code is evaluated, the VM would now process the job queue in order and it would block separately on each network request (single tread, even the VM), rather than performing the networking in parallel and performing the callbacks as soon as one request is processed.

I think it helps towards async builtin functions as it expands upon the ECMAScript standards but that would require making the VM async.

@lastmjs
Copy link
Contributor

lastmjs commented Mar 18, 2022

Even if it is still single-threaded run-to-completion, could we change the NativeFunctionSignature to allow a function that returns a Future? Even if it's blocking under-the-hood, would it still work?

So for example imagine creating a custom Rust function (that does an http request or something) that returns a Future exposed to boa as a function that returns a Promise in JS. Even if it's technically synchronous, this could be very useful for my use case.

@Razican
Copy link
Member

Razican commented Mar 18, 2022

Even if it is still single-threaded run-to-completion, could we change the NativeFunctionSignature to allow a function that returns a Future? Even if it's blocking under-the-hood, would it still work?

So for example imagine creating a custom Rust function (that does an http request or something) that returns a Future exposed to boa as a function that returns a Promise in JS. Even if it's technically synchronous, this could be very useful for my use case.

We should manage expectations here. It's important to understand that ECMAScript is single-threaded. Even its async/await is single-threaded always. This requires some guarantees (such as types not implementing Send) that collide with the Rust asynchronous runtime (everything must implement Send, and it's always treated as multithreaded, even with one thread).

So, NativeFunction will probably stay the same, but there might be an option at some point in the future to be able to return something like a NativePromise, which could be a JsValue. But this requires a lot of thought, and I'm not sure if it's even possible.

What is sure is that while we don't have asynchronous JavaScript, we cannot have asynchronous Rust. And even if/when implement asynchronous Rust, synchronous JavaScript and Rust must still be supported. So, this is a huge step towards enabling asynchronous JavaScript to run in Boa, and this could potentially open the doors to use asynchronous Rust native functions at some point, but it will be very, very tricky.

For now, you can use block_on() functions to call asynchronous Rust from a NativeFunction, but you will have to handle sending information between threads.

@lastmjs
Copy link
Contributor

lastmjs commented Mar 18, 2022

Thank you very much for the in-depth explanation. Unfortunately I am working in a constrained Wasm environment where block_on does not work, hence my great desire for asynchronus JS and Rust (since it blocks so many capabilities I am trying to give my users). Thanks!

@jasonwilliams
Copy link
Member

jasonwilliams commented Mar 23, 2022

Hopefully i can take a proper look at this this week

@Razican
Copy link
Member

Razican commented Mar 24, 2022

@aaronmunsters did you have the chance to look at the review comments?

@aaronmunsters
Copy link
Contributor Author

Hi there, unfortunately not yet. This week I've got another deadline coming up, but I'll get at it starting next week 😊

Copy link
Member

@jasonwilliams jasonwilliams left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like a great start to me. I agree it will need some tidying up from the comments. Job itself will need some documentation describing what it is and how it's used etc etc.

I like the test showing the value after completion.
Having something like mio handle our queue in future for tasks would be nice.
I think V8 doesn't actually deal with any of that, and Node just adds that logic on, i wonder if we'll eventually need a flag like we had with console.
Long term thinking but I wonder if we'll need to make a boa_runtime which uses boa_engine as a dependency.

@@ -709,10 +714,21 @@ impl Context {
self.realm.set_global_binding_number();
let result = self.run();
self.vm.pop_frame();
self.run_queued_jobs();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one thing that confused me, is in your example i would have expected count to be three. Because execute waits here for the promise job to finish before finishing. But it seems that's not the case. It looks like it returns 2 and you need to call it again (looking at your test).

Can someone explain that?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Promise constructor executes its code on creation, and then is queued on the job queue. After executing all the statements of the script, count === 2 and then is executed, making count === 3. However, the script already returned 2 as the evaluation result, since let result = self.run() executes before self.run_queued_jobs(). When the next execution occurs, count is already 3, so the evaluation returns 3.

@jedel1043
Copy link
Member

We should manage expectations here. It's important to understand that ECMAScript is single-threaded. Even its async/await is single-threaded always. This requires some guarantees (such as types not implementing Send) that collide with the Rust asynchronous runtime (everything must implement Send, and it's always treated as multithreaded, even with one thread).

That's not entirely true. The async rust model doesn't require that types implement Send, since that would make single-threaded executors infeasible. Instead, executors are the ones who require Send types IF the executor is multi-threaded. In other words, we can create !Send futures and run them in single-threaded executors with ease. E. g.
https://docs.rs/tokio/latest/tokio/task/struct.LocalSet.html
I would expect that, when we finally make the VM executor async, all builtin functions would be changed to return impl Future<Output=JsResult<T>> or even Ready<JsResult<T>> if we consider that it's not really necessary to return futures in builtin functions.

@eng-cc
Copy link
Contributor

eng-cc commented Apr 11, 2022

Whether there is progress in this feature, it is a very practical feature, I am looking forward to its completion.

Copy link
Member

@jedel1043 jedel1043 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the wait! I finally did an extensive review, and there are some details that should improve the semantics and readability of the code.

boa_engine/src/context/mod.rs Show resolved Hide resolved
}

impl JobCallback {
pub fn make_job_callback(callback: JsValue) -> Self {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should verify our invariants on creation, like checking that the input is a callable JsObject (which complements the other comment about storing a JsObject). This would allow us to skip the checks on call_job_callback and run.

@@ -0,0 +1,39 @@
use crate::{Context, JsResult, JsValue};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this should be moved to the promise module, but I'll like to hear your rationale about putting it on the root, since I'm still not sure.

boa_engine/src/builtins/promise/mod.rs Show resolved Hide resolved
Comment on lines +202 to +206
#[derive(Debug, Trace, Finalize)]
struct RejectResolveCaptures {
promise: JsObject,
already_resolved: JsObject,
}
Copy link
Member

@jedel1043 jedel1043 Apr 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can move this inside create_resolving_functions. This should limit its scope, since it's only used internally, and it would make it renameable to only Captures. Also applies to every ...Captures struct.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also being used in job.rs, so I left it as-is in #2107. Feel free to review it there.

boa_engine/src/builtins/promise/mod.rs Show resolved Hide resolved
boa_engine/src/builtins/promise/mod.rs Show resolved Hide resolved
boa_engine/src/builtins/promise/mod.rs Show resolved Hide resolved
boa_engine/src/builtins/promise/mod.rs Show resolved Hide resolved
@Razican
Copy link
Member

Razican commented Apr 26, 2022

@aaronmunsters did you have the chance to look at this?

@Razican
Copy link
Member

Razican commented Jun 8, 2022

Closing in favour of #2107.

@Razican Razican closed this Jun 8, 2022
bors bot pushed a commit that referenced this pull request Jun 15, 2022
This PR overrides #1923. It also removes the `queues` dependency added there, and rebases it to the latest `main` branch state.

It adds the following:

- A job queue (in `Context`)
- The constructor [`Promise`](https://tc39.es/ecma262/#sec-promise-executor)
- [`Promise.race`](https://tc39.es/ecma262/#sec-promise.race)
- [`Promise.reject`](https://tc39.es/ecma262/#sec-promise.reject)
- [`Promise.resolve`](https://tc39.es/ecma262/#sec-promise.resolve)
- [`get Promise [ @@species ]`](https://tc39.es/ecma262/#sec-get-promise-@@species)
- [`Promise.prototype [ @@toStringTag ]`](https://tc39.es/ecma262/#sec-promise.prototype-@@tostringtag)
- [`Promise.prototype.then`](https://tc39.es/ecma262/#sec-promise.prototype.then)
- [`Promise.prototype.finally`](https://tc39.es/ecma262/#sec-promise.prototype.finally)
- [`Promise.prototype.catch`](https://tc39.es/ecma262/#sec-promise.prototype.catch)
- The additional needed infrastructure
  - [`PerformPromiseThen ( promise, onFulfilled, onRejected [ , resultCapability ] )`](https://tc39.es/ecma262/#sec-performpromisethen)
  - [`TriggerPromiseReactions ( reactions, argument )`](https://tc39.es/ecma262/#sec-triggerpromisereactions)
  - [`PerformPromiseRace ( iteratorRecord, constructor, resultCapability, promiseResolve )`](https://tc39.es/ecma262/#sec-performpromiserace)
  - [`RejectPromise ( promise, reason )`](https://tc39.es/ecma262/#sec-rejectpromise)
  - [`FulfillPromise ( promise, value )`](https://tc39.es/ecma262/#sec-fulfillpromise)
  - [`IfAbruptRejectPromise ( value, capability )`](https://tc39.es/ecma262/#sec-ifabruptrejectpromise)
  - [`CreateResolvingFunctions ( promise )`](https://tc39.es/ecma262/#sec-createresolvingfunctions)
  - [`NewPromiseCapability ( C )`](https://tc39.es/ecma262/#sec-newpromisecapability)
  - [`NewPromiseReactionJob ( reaction, argument )`](https://tc39.es/ecma262/#sec-newpromisereactionjob)
  - [`NewPromiseResolveThenableJob ( promiseToResolve, thenable, then )`](https://tc39.es/ecma262/#sec-newpromiseresolvethenablejob)
  - [`PromiseResolve ( C, x )`](https://tc39.es/ecma262/#sec-promise-resolve)
- A test case showcasing the run-to-completion semantics.

An example program that shows the control flow with this addition is:
```javascript
new Promise((res, rej) => {
  console.log("A");
  res(undefined);
}).then((_) => console.log("B"));
console.log("C");
```
Which would output:
```
A
C
B
```
@raskad raskad removed this from the v0.16.0 milestone Sep 21, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
builtins PRs and Issues related to builtins/intrinsics enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants