I77537 StackDocsEnvironment & Energy
Related
How to Deploy a Fleet of 103 Electric Buses: Lessons from Swedish CitiesMajor Renewables and Battery Storage Projects Secure CIS Tenders as Coal Phase-Out LoomsHow to Assess Tesla's Ambitious Humanoid Robot Sales TargetFlutter Freezes Material and Cupertino Libraries Ahead of Migration to Standalone PackagesTop Green Deals This Week: Ride1Up E-Bike Price Drop, Anker SOLIX Flash Sale, and MoreStreamlining Massive Data Migrations: How Spotify Leveraged Honk, Backstage, and Fleet ManagementHow to Successfully Deploy Battery Electric Heavy Trucks in Your FleetKingman: Your Complete Guide to Route 66 EV Road Trip Planning

Mastering WebAssembly JSPI's New API: A Step-by-Step Guide

Last updated: 2026-05-04 04:14:56 · Environment & Energy

Introduction

WebAssembly’s JavaScript Promise Integration (JSPI) API has undergone a significant revision in Chrome release M126. The new API simplifies asynchronous interop for compiled C/C++ applications by removing explicit Suspender objects and the WebAssembly.Function constructor. Instead, it leverages the JavaScript/WebAssembly boundary to automatically suspend and resume computations. This guide walks you through using the updated JSPI API with Emscripten, covering everything from setup to practical implementation.

Mastering WebAssembly JSPI's New API: A Step-by-Step Guide

What You Need

  • Chrome M126 or later – the only browser currently supporting the new JSPI API.
  • Emscripten SDK (3.1.59 or newer) – includes JSPI support and the new API wrappers.
  • Node.js (optional) – for testing outside the browser.
  • A C/C++ project with synchronous APIs that you want to bridge to JavaScript promises (e.g., file I/O, network requests).
  • Basic familiarity with WebAssembly, Emscripten, and JavaScript promises.

Step-by-Step Guide

Step 1: Install and Configure Emscripten

Download the latest Emscripten SDK from emscripten.org and activate it:

git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh

Verify the installation with emcc --version. Ensure your version is 3.1.59 or above.

Step 2: Compile Your C/C++ Code with JSPI Support

Add the -sASYNCIFY and -sJSPI flags to your Emscripten compile command. For example:

emcc my_program.c -o my_program.html -sASYNCIFY -sJSPI

This tells Emscripten to instrument your code for asyncify (necessary for JSPI) and to include the JSPI runtime. If you have functions that call JavaScript promises, mark them with EM_ASYNC_JS:

EM_ASYNC_JS(int, fetch_data, (const char* url), {
  let response = await fetch(UTF8ToString(url));
  let text = await response.text();
  return stringToUTF8(text, $0, 1024);
});

Step 3: Create JSPI Wrappers for Your Exported Functions

The new API provides WebAssembly.createJSPI({ ... }) to wrap exported functions. No more explicit Suspender objects. In your JavaScript, after loading the module:

const importObject = {
  "env": {
    // your imported JS functions
  }
};

WebAssembly.instantiateStreaming(fetch('my_program.wasm'), importObject)
  .then(obj => {
    const { instance } = obj;
    // Wrap the exported function that may suspend
    const wrapped = WebAssembly.createJSPI({
      exports: instance.exports,
      exportNames: ['my_async_export']
    });
    // Now 'wrapped.my_async_export' returns a Promise
    wrapped.my_async_export().then(result => {
      console.log('Result:', result);
    });
  });

The createJSPI function automatically determines suspension boundaries based on the outermost WebAssembly call, so you don’t need to manage cut points manually.

Step 4: Call the Wrapped Export and Handle Promises

When you call the wrapped export, JSPI suspends the WebAssembly computation if the called JavaScript function returns a Promise. It resumes once the promise resolves. Example:

async function run() {
  const result = await wrapped.my_async_export();
  console.log('Done:', result);
}
run();

If your C function does not actually encounter a promise (e.g., it calls a synchronous JS function), JSPI does not suspend – a safe optimization that avoids unnecessary event loop trips.

Step 5: Test and Debug

Run your application in Chrome M126+. Open DevTools > Sources > WebAssembly and set breakpoints inside your C code. You can inspect the call stack during suspension. If something fails, check the console for JSPI-related errors (e.g., “JSPI: attempted to suspend while not in a wrapped export”). Ensure you have wrapped only the top-level exports that may suspend – wrapping internal functions can cause issues.

Tips for Success

  • Keep exports minimal – Only wrap the functions that directly or indirectly interact with asynchronous JavaScript. Unnecessary wrapping adds overhead.
  • Avoid nested suspensions – JSPI does not support recursive suspension. Make sure your call graph doesn’t call a wrapped export from within another wrapped export.
  • Use EM_ASYNC_JS carefully – Each such function creates a discontinuity that JSPI handles. For simple synchronous calls, use regular EM_JS.
  • Test on Chrome Canary – If M126 hasn’t rolled out to your stable channel, use Canary to access the latest JSPI features.
  • Monitor performance – JSPI adds overhead per suspension. Profile your app to ensure it’s acceptable for your use case.
  • Refer to the official spec – The JSPI specification details edge cases and the exact behavior of the new API.

With these steps, you can leverage the simplified JSPI API to bring your synchronous C/C++ WebAssembly modules into the asynchronous world of JavaScript promises – without manual Suspender management. Happy coding!