You can use the Fetch API to fetch .wasm file. The returned Promise instance of fetch is passed into WebAssembly.instantiateStreaming. For example:

WebAssembly.instantiateStreaming(fetch('program.wasm'), importObj)
           .then(prog => {
               console.log(prog.instance.exports.add(1, 2)); 
           });

WebAssembly.instantiateStreaming would take out the ArrayBuffer instance from the Promise object and instantiate the module. The following code has the same effect as the above.

fetch('program.wasm')
    .then(response => response.arrayBuffer())
    .then(wasm => WebAssembly.instantiate(wasm, importObj))
    .then(prog => {
        console.log(prog.instance.exports.add(1, 2)); 
    });

That’s to say, what we want is the ArrayBuffercontaining your WebAssembly module binary so you can use XMLHttpRequest even through it’s older than Fetch API.

const request = new XMLHttpRequest();
request.responseType = 'arraybuffer';
request.onload = function() {
    const wasm = request.response;
    WebAssembly.instantiate(wasm, importObj)
               .then(prog => {
                   console.log(prog.instance.exports.add(1, 2)); 
               });
};
request.open('GET', 'program.wasm');
request.send();

WebAssembly.instantiate accepts ArrayBuffer or TypedArray and returns Promise. After the success of the asynchronous operation, the promise’s value is an object containing two properties module and instance. The module property is an instance of WebAssembly.Module. It contains stateless WebAssembly code that has already been compiled by the browser. The instance property is an instance of WebAssembly.Instance. It contains all the exported WebAssembly functions that allow calling into WebAssembly code from JavaScript.

If only the instance of WebAssembly.Module is required, you may use WebAssembly.compileStreaming. It accepts the returned Promise of fetch and returns Promise. After completing the operation, the promise’s value is an instance of Module.

WebAssembly.compileStreaming(fetch('program.wasm'))
       .then(module => new WebAssembly.Instance(module, importObj))
       .then(instance => {   
           console.log(instance.exports.add(1, 2)); 
       });

As shown in the above, you can take the Module object as the argument to constructing the Instance object. In fact, WebAssembly.compileStreaming will take out the ArrayBuffer and use WebAssembly.compile to compile it to a WebAssembly.Module instance. The following code has the same effect as the above.

fetch('program.wasm')
    .then(response => response.arrayBuffer())
    .then(wasm => WebAssembly.compile(wasm))
    .then(module => new WebAssembly.Instance(module, importObj))
    .then(instance => {   
        console.log(instance.exports.add(1, 2)); 
    });

You can use a Module instance to construct a WebAssembly.Instance instance. If you have ArrayBuffer, you can construct a WebAssembly.Module object.

const module = new WebAssembly.Module(wasm);

Using new to construct WebAssembly.Instance or WebAssembly.Module is a blocking operation. WebAssembly.compile() and WebAssembly.instantiate() are asynchronous operations and return Promose.

You can use WebAssembly.validate() to validate a given typed array of WebAssembly binary code.