javascriptwebassemblyemscriptenasm.js

How to detect when you are ready to use functions loaded in asm.js module


I'm using emscripten to generate a file a.wasm.js that contains functions in C++ for encryption. The webassembly functions take in and return C-strings that hold JSON data. I want to wrap these asm.js functions for more convenient use in my client application. Unfortunately doing it directly inside body.onload doesn't seem to work so I'm using the following hack:

        <script>
            !function(e, t){
                console.log("Loading client...");
                var n = "a.wasm.js";
                if(!e.WebAssembly){
                    n = "a.js"
                }
                console.log("Script set to " + n);
                var o = t.createElement("script");
                o.async = true, o.type = "text/javascript", o.src = n, o.onerror = function(t) {
                    console.error("Script Error"), console.error(t), setTimeout(function() {
                        e.location.reload(!0)
                    }, 3e3)
                    
                };
                var r = t.getElementsByTagName("script")[0];
                r.parentNode.insertBefore(o, r)
            }(window, document);
        </script>
        <script>
            setTimeout(function(){
                let _generateKeyPair = Module.cwrap('generateKeyPair', 'string', null);
                window.generateKeyPair = function() {
                    return JSON.parse(_generateKeyPair());
                }
            }, 1000)
        </script>

Of course, the problem here is that I cannot use "generateKeyPair" until 1 second after the app is loaded.

Is there any callback I can hook in to to know that the asm.js Module object has fully loaded and I can start to use the functions defined within it?


Solution

  • One approach is to define and await a Module.ready Promise. This is injected during Emscripten compilation using the --post-js flag. (see full examples)

    <script src="wasm-module.js"></script>
    <script>
      Module.ready
        .then(api => console.log( api.sayHello() ))
        .catch(e => console.error('💩', e))
    </script>
    
    $ emcc \
      -O0 `# leave uncompressed for example` \
      -s WASM=1 \
      -s EXPORTED_FUNCTIONS="['_hello']" \
      -s EXTRA_EXPORTED_RUNTIME_METHODS="['cwrap']" \
      -o wasm-module.js \
      --post-js module-post.js \
      hello.c
    
    // hello.c
    
    char *hello() {
      return "Hello there. Welcome to WebAssembly.";
    }
    
    // module-post.js
    
    // Module.ready resolves when WASM instantiates. (ready is now a property and not function via @surma fork)
    Module.ready = new Promise(function(resolve, reject) {
      addOnPreMain(function() {
        var api = {
          sayHello: Module.cwrap('hello', 'string', [])
        };
        resolve(api);
      });
    
      // Propagate error to Module.ready.catch()
      // WARNING: this is a hack based Emscripten's current abort() implementation
      // and could break in the future.
      // Rewrite existing abort(what) function to reject Promise before it executes.
      var origAbort = this.abort;
      this.abort = function(what) {
        reject(Error(what));
        origAbort.call(this, what);
      }
    });