Skip to content

Comments

[jspi] Require async js functions when used with __async decorator.#25480

Closed
brendandahl wants to merge 1 commit intoemscripten-core:mainfrom
brendandahl:jspi-async-decorator
Closed

[jspi] Require async js functions when used with __async decorator.#25480
brendandahl wants to merge 1 commit intoemscripten-core:mainfrom
brendandahl:jspi-async-decorator

Conversation

@brendandahl
Copy link
Collaborator

The _emval_await library function is marked _emval_await__async: true, but the js function is not async. With memory64 enabled we auto convert to bigint and look for the async keyword (which is missing) to apply the await before creating the BigInt. With my changes __async will require an async js function, which signals the function is used with JSPI and the appropriate awaits are then inserted.

Fixes #25468


import assert from 'node:assert';
import * as fs from 'node:fs/promises';
import { isAsyncFunction } from 'node:util/types';
Copy link
Collaborator

Choose a reason for hiding this comment

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

Oh.. interesting. TIL about this.

emscripten_sleep__deps: ['$safeSetTimeout'],
emscripten_sleep__async: true,
emscripten_sleep: (ms) => Asyncify.handleSleep((wakeUp) => safeSetTimeout(wakeUp, ms)),
emscripten_sleep: async (ms) => Asyncify.handleSleep((wakeUp) => safeSetTimeout(wakeUp, ms)),
Copy link
Collaborator

Choose a reason for hiding this comment

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

What does flagging this async gain here? (it costs code size)

I understand the rationale for async is that one can await for the return value, but for functions that return void, isn't the async keyword dead code?

Though Asyncify and JSPI build modes itself do not need the functions to be flagged async do they? (in WebGPU JSPI support I went with this)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It really doesn't add anything here, but consistency. We have a few spots where we auto modify the library js (e.g. memory 64). It's much easier in those wrapper functions to assume we're in a async function and emit await code (also the await code is usually shorter).

@brendandahl brendandahl force-pushed the jspi-async-decorator branch 2 times, most recently from f8b8c87 to c8e1794 Compare October 6, 2025 22:07
@brendandahl brendandahl requested a review from sbc100 October 7, 2025 18:18
Copy link
Collaborator

@sbc100 sbc100 left a comment

Choose a reason for hiding this comment

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

lgtm % @juj's comment

@poretga99
Copy link

Can this PR proceed? We'd be more than happy if this landed in the main branch. We're currently patching released Emscripten manually.

@brendandahl brendandahl enabled auto-merge (squash) October 14, 2025 23:01
@curiousdannii
Copy link
Contributor

It's perfectly legitimate to return a promise instead, and sometimes that would result in cleaner code. This should be at most a warning, not a requirement.

@sbc100
Copy link
Collaborator

sbc100 commented Oct 16, 2025

It's perfectly legitimate to return a promise instead, and sometimes that would result in cleaner code. This should be at most a warning, not a requirement.

Right, but there is no harm in doing both, right? async functions can do return somepromise just fine right?

@curiousdannii
Copy link
Contributor

I guess.. though I think it would be slightly lower performance (probably not noticeable unless it's a really hot function.)

Hmm. If you did require async functions to be AsyncFunctions, then could you do away with the __async decorator?

@sbc100
Copy link
Collaborator

sbc100 commented Oct 16, 2025

I guess.. though I think it would be slightly lower performance (probably not noticeable unless it's a really hot function.)

According to AI there is not any extra overhead: Returning a promise from an async function in JavaScript does not create a second promise in the sense of a new, distinct promise object being generated on top of the one you explicitly return. Instead, the async function's inherent promise-returning nature integrates with the promise you return.

Take that with as many grains of salt as you like ..

The `_emval_await` library function is marked `_emval_await__async: true`,
but the js function is not async. With memory64 enabled we auto convert to
bigint and look for the async keyword (which is missing) to apply the await
before creating the BigInt. With my changes __async will require an
async js function, which signals the function is used with JSPI and the
appropriate awaits are then inserted.
@brendandahl brendandahl force-pushed the jspi-async-decorator branch from 98c6feb to e6bc1f1 Compare January 5, 2026 23:50
@sbc100
Copy link
Collaborator

sbc100 commented Jan 9, 2026

Can we land the fix for _emval_await withing requiring that all __async functions are marked with the async keyword?

That later sounds a little more controversial, and its also reasonable to write old school async functions that return a promise without the keyword right? (we have some of those in the codebase already)

@brendandahl
Copy link
Collaborator Author

Looks like this was fixed in the __async: auto pr.

auto-merge was automatically disabled January 13, 2026 23:56

Pull request was closed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Embind + MEMORY64: Cannot convert Promise to a BigInt

5 participants