Загрузка и запуск кода WebAssembly
Чтобы использовать WebAssembly в JavaScript, сначала нужно загрузить модуль в память перед компиляцией/созданием экземпляра. Эта статья содержит справочную информацию о различных механизмах, которые можно использовать для получения байт-кода WebAssembly, а также о том, как скомпилировать/создать экземпляр, а затем запустить его.
Какие есть варианты?
WebAssembly ещё не интегрирована с <script type='module'>
или ES2015 оператором import
, поэтому не существует пути, позволяющего использовать модули загрузки браузера для использования импорта.
Старые методы WebAssembly.compile
/WebAssembly.instantiate
требуют создания ArrayBuffer
, содержащего двоичный файл модуля WebAssembly после загрузки необработанных байтов, а затем скомпилировать/создать его экземпляр. Это аналог new Function(string)
, за исключением того, что мы заменяем строку символов (исходный код JavaScript) буфером байтов массива (исходный код WebAssembly).
Более новые методы WebAssembly.compileStreaming
/WebAssembly.instantiateStreaming
намного эффективнее - они выполняют свои действия непосредственно с необработанным потоком байтов, поступающих из сети, избавление от необходимости шага ArrayBuffer
.
Итак, как мы можем получить эти байты в буфер массива и скомпилировать? Следующие разделы объясняют.
Используя Fetch
Fetch - это удобный современный API для извлечения сетевых ресурсов.
Самый быстрый и эффективный способ получить модуль wasm - использовать более новый метод WebAssembly.instantiateStreaming()
, который может принять вызов fetch()
в качестве первого аргумента и будет обрабатывать загрузку, компиляцию и создание экземпляра модуля за один шаг, получая доступ к необработанному байтовому коду при его потоковой передаче с сервера:
WebAssembly.instantiateStreaming(fetch("simple.wasm"), importObject).then(
(results) => {
// Do something with the results!
},
);
Если бы мы использовали более старый метод WebAssembly.instantiate()
, который не работает в прямом потоке, нам потребовался бы дополнительный шаг преобразования преобразованного байт-кода в ArrayBuffer
, вот так:
fetch("module.wasm")
.then((response) => response.arrayBuffer())
.then((bytes) => WebAssembly.instantiate(bytes, importObject))
.then((results) => {
// Do something with the results!
});
Помимо перегрузок instantiate()
Функция WebAssembly.instantiate()
имеет две формы перегрузки - та, что показана выше, принимает байт-код для компиляции в качестве аргумента и возвращает Promise
, которое разрешается для объекта, содержащего оба объекта скомпилированного модуля, и экземпляр этого. Объект выглядит так:
{
module: Module; // The newly compiled WebAssembly.Module object,
instance: Instance; // A new WebAssembly.Instance of the module object
}
Примечание:
Обычно мы заботимся только об экземпляре, но полезно иметь модуль на тот случай, если мы хотим его кешировать, поделиться им с другим работником или окном через postMessage()
, или просто создать больше экземпляров.
Примечание:
Вторая форма перегрузки принимает в качестве аргумента объект WebAssembly.Module
и возвращает Promise
, непосредственно содержащее объект экземпляра, в качестве результата. См. Второй пример перегрузки.
Выполнение вашего кода WebAssembly
Когда у вас есть экземпляр WebAssembly, доступный в вашем JavaScript, вы можете начать использовать его возможности, которые были экспортированы через свойство WebAssembly.Instance.exports
. Ваш код может выглядеть примерно так:
WebAssembly.instantiateStreaming(fetch("myModule.wasm"), importObject).then(
(obj) => {
// Call an exported function:
obj.instance.exports.exported_func();
// or access the buffer contents of an exported memory:
var i32 = new Uint32Array(obj.instance.exports.memory.buffer);
// or access the elements of an exported table:
var table = obj.instance.exports.table;
console.log(table.get(0)());
},
);
Примечание: Для получения дополнительной информации о том, как работает экспорт из модуля WebAssembly, ознакомьтесь с разделами Использование JavaScript API WebAssembly, и Понимание текстового формата WebAssembly.
Используя XMLHttpRequest
XMLHttpRequest
несколько старше, чем Fetch, но всё же может успешно использоваться для получения типизированного массива. Опять же, если предположить, что наш модуль называется simple.wasm
:
- Создайте новый экземпляр
XMLHttpRequest()
и используйте его методopen()
для открытия запроса, задав для метода запроса значениеGET
и указав путь к файлу, который мы хотим получить. - Ключевой частью этого является установка типа ответа
'arraybuffer'
с помощью свойстваresponseType
. - Затем отправьте запрос с помощью
XMLHttpRequest.send()
. - Затем мы используем обработчик событий
onload
для вызова функции после завершения загрузки ответа - в этой функции мы получаем буфер массива изresponse
и затем передайте это в наш методWebAssembly.instantiate()
, как мы это делали с Fetch.
Финальный код выглядит так:
request = new XMLHttpRequest();
request.open("GET", "simple.wasm");
request.responseType = "arraybuffer";
request.send();
request.onload = function () {
var bytes = request.response;
WebAssembly.instantiate(bytes, importObject).then((results) => {
results.instance.exports.exported_func();
});
};
Примечание: Можно увидеть пример этого в действии в xhr-wasm.html.