Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
I’m publishing this Draft PR so folks can experiment with this. @torch2424 I’d love to hear what you think about this in general. I’m sure the code needs some cleaning up, but I’m pretty excited about having Web API Integration work almost out-of-the-box.
This PR gives as-bind the ability to handle async functions & promises in imported functions. This allows AssemblyScript modules to integrate with asynchronous web APIs like
fetch()
, idb,createImageBitmap
, WebUSB, WebBluetooth, etc. You name it!Example
Screenshot
Here’s a gist with a working build system.
(In the future, you could even use
externref
to handResponse, allowing you to import
fetch()` directly.)How to use it
The heavy lifting is done under the hood by [Asyncify]. All you need to do is add
--runPasses="asyncify"
to yourasc
invocation, all the rest happens inside as-bind automatically.If you want to play around with this, here are step-by-step instructions:
promise-support
npm install && npm run build
npm link /path/to/as-bind/clone/from/step/1
--runPasses="asyncify"
to yourasc
invocationHow does it work
WebAssembly is synchronous, and functions that are imported by your Wasm from the host environent (i.e. from JS) are expected to return their value synchronously. But the web is asynchronous. Making something synchronous look asynchronous is easy, but the other way is near impossible.
Asyncify is a tool (more specifically: a binaryen pass) that effectively allows you to “pause” WebAssembly. Through this, you can make make JS that’s asynchronous look synchronous to WebAssembly. The trick is to pause Wasm when an imported function returns a promise, wait until the promise has resolved and then unpause to continue with the resolved value. From the perspective of WebAssembly, it’s as if the pause never happened.
To facilitate the pausing and later resuming, Asyncify stores the current stack of the Wasm VM to memory. To avoid any corruption, as-bind allocates a block of memory via the runtime and prevents it from getting GC’d. How much memory is actually needed, depends on the number of function parameters and function calls you have on your stack. By default, as-bind reserves 8KiB per paused Wasm call. If you to change that number, add this just after instantiation:
instance.asyncifyStorageSize = 8 * 1024;
.What does it not do
This does not add support for
async
/await
to AssemblyScript itself, nor is it a full integration of AS into JavaScript’s event loop.Noteworthy limitations
Asyncify has a small performance and binary size impact. I have not seen it become a problem, but it’s something to be aware of. Because Asyncify rewrites your Wasm binary, it will probably break source maps. Also, Asyncify currently can’t handle
externref
et al.