Title: Asyncify and JSPI: Stack Switching in PHP WebAssembly
Published: July 15, 2026
Last modified: July 16, 2026

---

# Asyncify and JSPI: Stack Switching in PHP WebAssembly

## In this article

 * [Asyncify crashes](https://developer.wordpress.org/playground/developers/architecture/wasm-asyncify/?output_format=md#asyncify-crashes)
 * [Fixing Asyncify crashes](https://developer.wordpress.org/playground/developers/architecture/wasm-asyncify/?output_format=md#fixing-asyncify-crashes)
 * [JSPI: The Modern Alternative to Asyncify](https://developer.wordpress.org/playground/developers/architecture/wasm-asyncify/?output_format=md#jspi-the-modern-alternative-to-asyncify)
 * [Binary Size Optimization with MAIN_MODULE=2](https://developer.wordpress.org/playground/developers/architecture/wasm-asyncify/?output_format=md#binary-size-optimization-with-main_module2)

[ Back to top](https://developer.wordpress.org/playground/developers/architecture/wasm-asyncify/?output_format=md#wp--skip-link--target)

[Asyncify](https://emscripten.org/docs/porting/asyncify.html) lets synchronous C
or C++ code interact with asynchronous JavaScript. Technically, it saves the entire
C call stack before yielding control back to JavaScript, and then restores it when
the asynchronous call is finished. This is called **stack switching**.

Networking support in the WebAssembly PHP build is implemented using Asyncify. When
PHP makes a network request, it yields control back to JavaScript, which makes the
request, and then resumes PHP when the response is ready. It works well enough that
PHP build can request web APIs, install composer packages, and even connect to a
MySQL server.

## 󠀁[Asyncify crashes](https://developer.wordpress.org/playground/developers/architecture/wasm-asyncify/?output_format=md#asyncify-crashes)󠁿

Stack switching requires wrapping all C functions that may be found at a call stack
at a time of making an asynchronous call. Blanket-wrapping of every single C function
adds a **significant** overhead, which is why we maintain a list of specific function
names:

https://github.com/WordPress/wordpress-playground/blob/15a660940ee9b4a332965ba2a987f6fda0c159b1/
packages/php-wasm/compile/Dockerfile#L624-L632

Unfortunately, missing even a single item from that list results in a WebAssembly
crash whenever that function is a part of the call stack when an asynchronous call
is made. It looks like this:

![A screenshot of an asyncify error in the terminal](https://i0.wp.com/raw.githubusercontent.
com/WordPress/wordpress-playground/refs/heads/trunk/packages/docs/site/static/img/
developers/asyncify-error.webp?ssl=1)

Asyncify can auto-list all the required C functions when built without `ASYNCIFY_ONLY`,
but that auto-detection is overeager and ends up listing about 70,000 C functions
which increases the startup time to 4.5s. That’s why we maintain the list manually.

If you are interested in more details, [see GitHub issue 251](https://github.com/WordPress/wordpress-playground/issues/251).

## 󠀁[Fixing Asyncify crashes](https://developer.wordpress.org/playground/developers/architecture/wasm-asyncify/?output_format=md#fixing-asyncify-crashes)󠁿

[Pull Request 253](https://github.com/WordPress/wordpress-playground/pull/253) adds
a `fix-asyncify` command that runs a specialized test suite and automatically adds
any identified missing C functions to the `ASYNCIFY_ONLY` list.

If you run into a crash like the one above, you can fix it by:

 1. Identifying a PHP code path that triggers the crash – the stack trace in the terminal
    should help with that.
 2. Adding a test case that triggers a crash to `packages/php-wasm/node/src/test/php-
    asyncify.spec.ts`
 3. Running: `npm run fix-asyncify`
 4. Committing the test case, the updated `Dockerfile`, and the rebuilt `PHP.wasm`

## 󠀁[JSPI: The Modern Alternative to Asyncify](https://developer.wordpress.org/playground/developers/architecture/wasm-asyncify/?output_format=md#jspi-the-modern-alternative-to-asyncify)󠁿

The [JavaScript Promise Integration (JSPI)](https://v8.dev/blog/jspi) API handles
stack switching natively in V8, eliminating the need for Asyncify’s function wrapping.
WordPress Playground now ships JSPI builds alongside Asyncify builds for all PHP
versions (7.4–8.5).

**Current status:**

 * The Playground CLI **auto-detects JSPI support** and enables it automatically—
   no manual flags needed
 * Node.js 23+ supports JSPI natively; Node.js 22 requires the `--experimental-wasm-
   jspi` flag (handled automatically by the CLI)
 * Node.js 24+ is expected to have JSPI unflagged
 * Browser support varies: JSPI is available in Chrome/Chromium-based browsers behind
   flags

## 󠀁[Binary Size Optimization with MAIN_MODULE=2](https://developer.wordpress.org/playground/developers/architecture/wasm-asyncify/?output_format=md#binary-size-optimization-with-main_module2)󠁿

Both Asyncify and JSPI builds are compiled with Emscripten’s `MAIN_MODULE=2` flag,
which performs dead code elimination on exported symbols. Only symbols that dynamic
extensions actually need are exported.

**Impact:**

 * Total binary size reduced by **122 MB** (13.7%)
 * `.wasm` files reduced by **109 MB** (16%)
 * JavaScript glue code reduced by **14.5 MB** (63%)

This optimization applies across all PHP versions (7.4–8.5) for both Node.js and
Web targets. The exported symbol list is centrally managed in the Dockerfile, with
conditional exports for specific extensions (e.g., `__c_longjmp` for Xdebug, `_wasm_recv`
for Memcached).

First published

July 15, 2026

Last updated

July 16, 2026

Edit article

[ Improve it on GitHub: Asyncify and JSPI: Stack Switching in PHP WebAssembly ](https://raw.githubusercontent.com/WordPress/wordpress-playground/trunk/packages/docs/site/docs/developers/23-architecture/07-wasm-asyncify.md)

Changelog

[ See list of changes: Asyncify and JSPI: Stack Switching in PHP WebAssembly ](https://developer.wordpress.org/playground/developers/architecture/wasm-asyncify/?output_format=md#)

[  Previous: PHP Filesystem](https://developer.wordpress.org/playground/developers/architecture/wasm-php-filesystem/)

[  Next: Running PHP apps in the browser with ServiceWorkers and Worker Threads](https://developer.wordpress.org/playground/developers/architecture/browser-concepts/)