-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Direct return from async function takes too many ticks #2770
Comments
I've noticed this as well, would be great if it could be improved. |
Digging through the fix for this, I've actually found that our When you call
The A+ spec does not mention creating a new job before calling Essentially, we create a Job for wrapping This means that (A) This is the reason we had to patch Await in #1250 to use |
This aligns the behavior of handling thenables with the Promises A+ spec, which says when the resolution value is a thenable, you're supposed to synchronously call `thenable.then(onRes, onRej)` ([Step 2.3.3.3][call-then]). Given the example code: ```javascript new Promise(resolve => { resolve(thenable) }); ``` The current behavior requires 2 ticks for the outer promise to fully resolve. One tick is created tick is created by the Promise Resolving Functions (and is removed in this PR), and one tick is created by the wrapping of the `onRes`/`onRej` callbacks passed to [`Promise.p.then`][then]. This made it noticeably slower to resolve with a thenable then to invoke the thenable's `.then` directly: `thenable.then(onRes, onRej)` only requires a single tick (for the wrapped `onRes`/`onRej`). With this change, we could revert tc39#1250 without slowing down `Await`. Fixes tc39#2770. [call-then]: https://promisesaplus.com/#point-56 [then]: https://tc39.es/ecma262/multipage/control-abstraction-objects.html#sec-performpromisethen
Oh, I'd love to see that! If you're open to fundamentally changing the promise |
This aligns the behavior of handling thenables with the Promises A+ spec, which says when the resolution value is a thenable, you're supposed to synchronously call `thenable.then(onRes, onRej)` ([Step 2.3.3.3][call-then]). Given the example code: ```javascript new Promise(resolve => { resolve(thenable) }); ``` The current behavior requires 2 ticks for the outer promise to fully resolve. One tick is created by the Promise Resolving Functions (and is removed in this PR), and one tick is created by the wrapping of the `onRes`/`onRej` callbacks passed to [`Promise.p.then`][then]. This made it noticeably slower to resolve with a thenable than to invoke the thenable's `.then` directly: `thenable.then(onRes, onRej)` only requires a single tick (for the wrapped `onRes`/`onRej`). With this change, we could revert tc39#1250 without slowing down `Await`. Fixes tc39#2770. [call-then]: https://promisesaplus.com/#point-56 [then]: https://tc39.es/ecma262/multipage/control-abstraction-objects.html#sec-performpromisethen
This makes promises & async take two ticks instead of three ticks. See tc39/ecma262#1250 See tc39/ecma262#2770 See tc39/ecma262#2772 50% faster when measuring call overhead. Similar improvements for https://github.com/v8/promise-performance-tests and when measuring with the following snippet: ```js import { run, bench } from "mitata"; bench("sync", () => {}); bench("async", async () => {}); run(); ```
I have a somewhat long breakdown as to why it's 3 ticks in a StackOverflow here Some key snippets are:
If we adopt the same exact logic (optimization) to |
Promise resolution order changes in some instances, resulting in different orders for some errors within the errors array, as well as in different values of hasNext within incremental delivery payloads. This PR introduces an async `completePromisedValue` helper function rather than using a promise chain (see below links). https://github.com/tc39/proposal-faster-promise-adoption tc39/ecma262#2770 tc39/ecma262#2772 tc39/ecma262#1250 https://v8.dev/blog/fast-async Depends on #3793
Description:
In #1250, we reduced the number of ticks that awaiting a native promise value (from 3 ticks to 1 tick). However, returning from an async function can still take 3 ticks (somehow, I don't really understand it).
eshost Output:
(I believe that Hermes and JSC are incorrect here, and the other impls are technically correct. But none of the impls have the behavior I expect.)
✅ The "returnDirectPrimitive" branch, we have the value immediately and should only be waiting 1 tick for the chained
then
to fire.✅ The "returnAwaitPrimitive" branch, we should wait 1 tick to extract the value out of the implicitly created promise of
1
, immediately return the value, then 1 tick for the chainedthen
to fire.❌ The "returnDirectPromisePrimitive" branch. There's disagreement here between impls, and none of them behave like I expected. My thinking is that this should be just like "returnAwaitPrimitive", where we need only 2 ticks (1 tick for the async function to assimilate the value from the promise, and 1 tick for the chained
then
to fire). But somehow, it seems we should be waiting 3 ticks?! Is this creating a promise, wrapping it with another promise, then assimilating that promise (similar to #1250)? If so, we should fix this to take 2 ticks. (Hermes seems to be directly returning the promise with extracting?)✅ Finally we have the "returnAwaitPromisePrimitive", and this reenforces my belief that "returnDirectPromisePrimitive" is incorrect. In this branch, we wait 1 tick to extract the value from already resolved promise, immediately return the value, and 1 tick for the chained
then
to fire. (JSC seems to still have the legacy behavior we changed in #1250?)The text was updated successfully, but these errors were encountered: