Загрузка и запуск Wasm из JavaScript
Учимся подключать Wasm-модуль из JavaScript и вызывать его функции.
WebAssembly.instantiate — это функция JS, которая компилирует байты модуля и создаёт работающий экземпляр с доступными экспортами.
Три шага загрузки
Чтобы запустить Wasm из браузера, нужно: получить байты .wasm, скомпилировать и инстанцировать их, затем вызвать экспортированные функции. Самый современный способ — потоковая компиляция прямо из ответа сети:
// браузер: загрузка и запуск .wasm
const response = await fetch("add.wasm");
const { instance } = await WebAssembly.instantiateStreaming(response, {});
const result = instance.exports.add(2, 3);
console.log(result); // 5
Помечаем этот блок как language-text, потому что fetch и WebAssembly требуют браузерного окружения и реального файла — в песочнице урока он не запустится. Но логика именно такая.
Универсальный способ через байты
Если потоковая загрузка недоступна, сначала берут ArrayBuffer, потом инстанцируют:
const response = await fetch("add.wasm");
const bytes = await response.arrayBuffer();
const { instance } = await WebAssembly.instantiate(bytes, {});
console.log(instance.exports.add(10, 20)); // 30
Передача импортов
Второй аргумент instantiate — это объект импортов. Если модуль импортирует console.log, мы передаём его реализацию здесь:
const importObject = {
console: {
log: (x) => console.log("из Wasm:", x)
}
};
const { instance } = await WebAssembly.instantiate(bytes, importObject);
instance.exports.run();
Понимаем вызов изнутри на JS
Сам вызов Wasm-функции из JS выглядит как обычный вызов JS-функции. Покажем эквивалент того, что делает instance.exports.add, обычной функцией — числа передаются напрямую и быстро:
// то, как выглядит вызов экспорта Wasm со стороны JS
const exportsAdd = (a, b) => a + b; // имитация instance.exports.add
console.log("add(2, 3) =", exportsAdd(2, 3));
console.log("add(100, 250) =", exportsAdd(100, 250));
Вывод:
add(2, 3) = 5 add(100, 250) = 350
В Node.js
В Node всё похоже, только байты читают из файла через модуль fs:
const fs = require("fs");
const bytes = fs.readFileSync("add.wasm");
const { instance } = await WebAssembly.instantiate(bytes, {});
console.log(instance.exports.add(2, 3));
Как работает под капотом
За кулисами instantiate делает два дела: компилирует байты в машинный код (это можно сделать заранее через WebAssembly.compile) и инстанцирует — создаёт экземпляр с собственной памятью, таблицами и связанными импортами. instantiateStreaming начинает компиляцию ещё до того, как файл полностью скачался, экономя время. Возвращается объект с module (скомпилированный код, можно переиспользовать) и instance (живой экземпляр с exports).
Частые ошибки
- Неверный MIME-тип для Streaming — сервер должен отдавать
.wasmсContent-Type: application/wasm, иначеinstantiateStreamingругается. - Забыли await —
instantiateвозвращает Promise; безawaitполучите не результат, а сам Promise. - Не передали импорты — если модуль их ждёт, инстанцирование упадёт.
Итоги
- Поток: получить байты →
WebAssembly.instantiate(Streaming)→ вызватьinstance.exports. - Импорты передаются вторым аргументом как объект.
instantiateStreamingкомпилирует на лету, экономя время.- Вызов экспорта из JS выглядит как обычный вызов функции.