Laden und Ausführen von WebAssembly-Code
Um WebAssembly in JavaScript zu verwenden, müssen Sie zuerst Ihr Modul in den Speicher laden, bevor Sie es kompilieren/instanziieren. Dieser Artikel bietet eine Referenz für die verschiedenen Mechanismen, die verwendet werden können, um WebAssembly-Bytecode abzurufen, sowie die Schritte zum Kompilieren/Instanziieren und Ausführen.
Was sind die Optionen?
WebAssembly ist noch nicht mit <script type='module'>
oder import
-Anweisungen integriert, daher gibt es keinen Weg, dass der Browser Module für Sie über Importe abruft.
Die älteren Methoden WebAssembly.compile
/WebAssembly.instantiate
erfordern, dass Sie ein ArrayBuffer
erstellen, welches Ihr WebAssembly-Modul-Binär nach dem Abrufen der Rohbytes enthält, und es dann kompilieren/instanziieren. Dies ist analog zu new Function(string)
, außer dass wir eine Zeichenkette (JavaScript-Quellcode) durch einen Array-Buffer von Bytes (WebAssembly-Quellcode) ersetzen.
Die neueren Methoden WebAssembly.compileStreaming
/WebAssembly.instantiateStreaming
sind viel effizienter — sie führen ihre Aktionen direkt auf dem Rohdatenstrom von Bytes aus, die aus dem Netzwerk kommen, und beseitigen die Notwendigkeit für den ArrayBuffer
-Schritt.
Wie bekommen wir diese Bytes also in einen Array-Buffer und kompiliert? Die folgenden Abschnitte erklären es.
Verwendung von Fetch
Fetch ist eine bequeme, moderne API zum Abrufen von Netzwerkressourcen.
Der schnellste und effizienteste Weg, ein Wasm-Modul abzurufen, ist die Verwendung der neueren Methode WebAssembly.instantiateStreaming()
, die einen fetch()
-Aufruf als erstes Argument nehmen kann und das Abrufen, Kompilieren und Instanziieren des Moduls in einem Schritt übernimmt, wobei der Rohbyte-Code abgerufen wird, während er vom Server gestreamt wird:
WebAssembly.instantiateStreaming(fetch("simple.wasm"), importObject).then(
(results) => {
// Do something with the results!
},
);
Wenn wir die ältere Methode WebAssembly.instantiate()
verwenden würden, die nicht auf dem direkten Stream funktioniert, bräuchten wir einen zusätzlichen Schritt, um den abgerufenen Byte-Code in ein ArrayBuffer
zu konvertieren, wie folgt:
fetch("module.wasm")
.then((response) => response.arrayBuffer())
.then((bytes) => WebAssembly.instantiate(bytes, importObject))
.then((results) => {
// Do something with the results!
});
Nebenbemerkung zu Overloads von instantiate()
Die Funktion WebAssembly.instantiate()
hat zwei Überladungsformen — die oben gezeigte nimmt den Byte-Code zum Kompilieren als Argument und gibt ein Promise zurück, das zu einem Objekt mit sowohl dem kompilierten Modulobjekt als auch einer instanziierten Instanz desselben aufgelöst wird. Das Objekt sieht so aus:
{
module: Module, // The newly compiled WebAssembly.Module object,
instance: Instance, // A new WebAssembly.Instance of the module object
}
Hinweis:
Normalerweise interessieren wir uns nur für die Instanz, aber es ist nützlich, das Modul zu haben, falls wir es zwischenspeichern, mit einem anderen Worker oder Fenster per postMessage()
teilen oder mehr Instanzen erstellen wollen.
Hinweis:
Die zweite Überladungsform nimmt ein WebAssembly.Module
-Objekt als Argument und gibt ein Promise zurück, das direkt das Instanzobjekt als Ergebnis enthält. Siehe das Beispiel für die zweite Überladung.
Ausführen Ihres WebAssembly-Codes
Sobald Sie Ihre WebAssembly-Instanz in Ihrem JavaScript verfügbar haben, können Sie die Funktionen, die über die WebAssembly.Instance.exports
-Eigenschaft exportiert wurden, nutzen. Ihr Code könnte etwa so aussehen:
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:
const dv = new DataView(obj.instance.exports.memory.buffer);
// or access the elements of an exported table:
const table = obj.instance.exports.table;
console.log(table.get(0)());
},
);
Hinweis: Für weitere Informationen darüber, wie das Exportieren aus einem WebAssembly-Modul funktioniert, lesen Sie Verwendung der WebAssembly-JavaScript-API und Verständnis des WebAssembly-Textformats.
Verwendung von XMLHttpRequest
XMLHttpRequest
ist etwas älter als Fetch, kann aber immer noch problemlos verwendet werden, um ein typisiertes Array zu erhalten. Angenommen, unser Modul heißt simple.wasm
:
- Erstellen Sie eine neue Instanz von
XMLHttpRequest()
und verwenden Sie dieopen()
-Methode, um eine Anforderung zu öffnen, indem Sie die Anforderungsmethode aufGET
setzen und den Pfad zur Datei angeben, die wir abrufen möchten. - Der wesentliche Teil hierbei ist es, den Antworttyp auf
'arraybuffer'
mit der EigenschaftresponseType
festzulegen. - Senden Sie anschließend die Anforderung mit
XMLHttpRequest.send()
. - Verwenden Sie dann den
load
-Ereignishandler, um eine Funktion aufzurufen, wenn die Antwort vollständig heruntergeladen wurde — in dieser Funktion erhalten wir den Array-Buffer aus der Eigenschaftresponse
und geben ihn dann in unsere MethodeWebAssembly.instantiate()
ein, wie wir es mit Fetch getan haben.
Der endgültige Code sieht so aus:
const request = new XMLHttpRequest();
request.open("GET", "simple.wasm");
request.responseType = "arraybuffer";
request.send();
request.onload = () => {
const bytes = request.response;
WebAssembly.instantiate(bytes, importObject).then((results) => {
results.instance.exports.exported_func();
});
};
Hinweis: Sie können ein Beispiel dafür in Aktion in xhr-wasm.html sehen.