By Danny Guo

Translation: Crazy geek

Original text: blog.logrocket.com/the-introdu…

Reproduced without permission

WebAssembly (Wasm) is a relatively new feature in Web browsers, but it expands the functional potential of the Web as a service application platform.

Learning to use WebAssembly can be a difficult process for Web developers, but AssemblyScript offers a solution. Let’s first look at why WebAssembly is such a promising technology and then look at how AssemblyScript can tap its potential.

WebAssembly

WebAssembly is a low-level language for browsers that provides developers with goals for compiling the Web in addition to JavaScript. It enables web site code to run at near-native speed in a secure sandbox environment.

It was developed based on a design consensus represented by all the major browsers (Chrome, Firefox, Safari, and Edge) that now support WebAssembly.

WebAssembly is delivered in binary format, which means WebAssembly has an advantage over JavaScript in both size and load time. But it also has an easy-to-understand textual representation.

When WebAssembly was first released, some developers thought it might eventually replace JavaScript as the primary language of the Web. But it’s best to think of WebAssembly as a new tool that integrates well with existing Web platforms, which is its high-level goal.

Rather than replacing JavaScript’s existing use cases, WebAssembly is attracting more people because it introduces new use cases. Currently WebAssembly doesn’t have direct access to the DOM, and most sites want to use JavaScript, which has been optimized for years to be pretty fast. The following is an example of a list of possible WebAssembly use cases:

  • The game
  • Scientific visualization and simulation
  • CAD application
  • Image/video editing

What these applications have in common is that they are often thought of as desktop applications. By providing near-native performance for CPU-intensive tasks, WebAssembly makes it possible to migrate these programs to the Web.

Existing sites can also benefit from WebAssembly. Figma(www.figma.com/) provides a real-world example that dramatically reduces its load time by using WebAssembly. If your Site uses code that does a lot of computation, you can replace it with WebAssembly to improve performance.

Maybe now you’re interested in how to use WebAssembly. You can learn the language itself and write it directly, but it is actually intended to be compiled for other languages. It is designed to have good support for C and C++, and Rust has also invested heavily in the experimental support added to the Go language in Version 1.11.

But maybe you don’t want to learn or use one of these languages in order to use WebAssembly. That’s why AssemblyScript exists.

AssemblyScript

AssemblyScript is a compiler that converts TypeScript to WebAssembly. TypeScript, developed by Microsoft, adds types to JavaScript. AssemblyScript has become quite popular, even for those unfamiliar with it, allowing only a limited subset of TypeScript features, so it doesn’t take much to get started.

Because it is so similar to JavaScript, AssemblyScript makes it easy for Web developers to integrate WebAssembly into their sites without having to use a completely different language.

The trial

Let’s write our first AssemblyScript module (all of the code below is available on GitHub). We need a minimum node.js version of 8 to get [WebAssembly support](developer.mozilla.org/zh-CN/docs/… / Global_Objects/WebAssembly#Browser_compatibility)

Go to an empty directory, create a package.json file, and install AssemblyScript. Note that we need to install directly from its GitHub repository. It has not been released on NPM because AssemblyScript developers have not considered whether the compiler is ready for widespread use.

mkdir assemblyscript-demo
cd assemblyscript-demo
npm init
npm install --save-dev github:AssemblyScript/assemblyscript
Copy the code

Generate scaffold files using the asinit command:

npx asinit .
Copy the code

Our package.json should now contain the following script:

{
  "scripts": {
    "asbuild:untouched": "asc assembly/index.ts -b build/untouched.wasm -t build/untouched.wat --sourceMap --validate --debug"."asbuild:optimized": "asc assembly/index.ts -b build/optimized.wasm -t build/optimized.wat --sourceMap --validate --optimize"."asbuild": "npm run asbuild:untouched && npm run asbuild:optimized"}}Copy the code

The top-level index.js looks like this:

const fs = require("fs");
const compiled = new WebAssembly.Module(fs.readFileSync(__dirname + "/build/optimized.wasm"));
const imports = {
  env: {
    abort(_msg, _file, line, column) {
       console.error("abort called at index.ts:" + line + ":"+ column); }}}; Object.defineProperty(module,"exports", {
  get: () => new WebAssembly.Instance(compiled, imports).exports
});
Copy the code

It allows us to require a WebAssembly module just as easily as a normal JavaScript module.

The Assembly directory contains our AssemblyScript source code. The generated example is a simple addition function.

export function add(a: i32, b: i32) :i32 {
  return a + b;
}
Copy the code

Function signatures, like in TypeScript, use i32 because AssemblyScript uses WebAssembly-specific integer and floating point types instead of TypeScript’s generic number type.

Let’s build an example.

npm run asbuild
Copy the code

The build directory should now contain the following files:

optimized.wasm
optimized.wasm.map
optimized.wat
untouched.wasm
untouched.wasm.map
untouched.wat
Copy the code

We get a normal build and an optimized build. For each build, there is a.wasm binary, a.wasM.map source map, and a.wat text representation of the binary. The text representation is meant to be read, but now we don’t need to read or understand it — one of the purposes of AssemblyScript is that we don’t need to use the original WebAssembly.

Start Node and use the compiled module like any other module.

$node Welcome to node.js v12.10.0.type".help" for more information.
> const add = require('./index').add;
undefined
> add(3, 5)
8
Copy the code

That’s all you need to call WebAssembly from Node!

Adding a monitoring script

For development purposes, I recommend that you automatically rebuild the module with Onchange every time you change the source code, because AssemblyScript does not yet include monitor mode.

npm install --save-dev onchange
Copy the code

Add an asbuild: Watch script to package.json. Include -i flag to run the initial build immediately after running the command.

{
  "scripts": {
    "asbuild:untouched": "asc assembly/index.ts -b build/untouched.wasm -t build/untouched.wat --sourceMap --validate --debug"."asbuild:optimized": "asc assembly/index.ts -b build/optimized.wasm -t build/optimized.wat --sourceMap --validate --optimize"."asbuild": "npm run asbuild:untouched && npm run asbuild:optimized"."asbuild:watch": "onchange -i 'assembly/**/*' -- npm run asbuild"}}Copy the code

Now you can run Asbuild: Watch instead of constantly re-running Asbuild.

performance

Let’s write a basic benchmark to see what kind of performance gains can be achieved. WebAssembly specializes in CPU intensive tasks such as numerical computation, so we use a function to determine whether integers are prime.

Our reference implementation is shown below. This is a naive solution to violence, since our goal is to perform a lot of calculations.

function isPrime(x) {
    if (x < 2) {
        return false;
    }

    for (let i = 2; i < x; i++) {
        if (x % i === 0) {
            return false; }}return true;
}
Copy the code

The equivalent version of AssemblyScript requires only a few type annotations:

function isPrime(x: u32): bool {
    if (x < 2) {
        return false;
    }

    for (let i: u32 = 2; i < x; i++) {
        if (x % i === 0) {
            return false; }}return true;
}
Copy the code

We’ll use benchmark.js.

npm install --save-dev benchmark
Copy the code

Create a benchmark. Js:

const Benchmark = require('benchmark');

const assemblyScriptIsPrime = require('./index').isPrime;

function isPrime(x) {
    for (let i = 2; i < x; i++) {
        if (x % i === 0) {
            return false; }}return true;
}

const suite = new Benchmark.Suite;
const startNumber = 2;
const stopNumber = 10000;

suite.add('AssemblyScript isPrime'.function () {
    for (let i = startNumber; i < stopNumber; i++) {
        assemblyScriptIsPrime(i);
    }
}).add('JavaScript isPrime'.function () {
    for (let i = startNumber; i < stopNumber; i++) {
        isPrime(i);
    }
}).on('cycle'.function (event) {
    console.log(String(event.target));
}).on('complete'.function () {
    const fastest = this.filter('fastest');
    const slowest = this.filter('slowest');
    const difference = (fastest.map('hz') - slowest.map('hz')) / slowest.map('hz') * 100;
    console.log(`${fastest.map('name')} is ~${difference.toFixed(1)}% faster.`);
}).run();
Copy the code

Running Node Benchmark on my machine yields the following results:

AssemblyScript isPrime x 74.00 ops/sec ±0.43% (76 runs sampled)
JavaScript isPrime x 61.56 ops/sec ±0.30% (64 runs sampled)
AssemblyScript isPrime is ~20.2% faster.
Copy the code

Please note that this test is a Microbenchmark and should be read with caution.

For some of the more AssemblyScript benchmarks, I recommend you look at the WasmBoy benchmark and wave Equation benchmark.

Load module

Next, use our module in the site.

Create index.html first:

<! DOCTYPE html> <html> <head> <meta charset="utf-8" />
        <title>AssemblyScript isPrime demo</title>
    </head>
    <body>
        <form id="prime-checker">
            <label for="number">Enter a number to check if it is prime:</label>
            <input name="number" type="number" />
            <button type="submit">Submit</button>
        </form>

        <p id="result"></p>

        <script src="demo.js"></script>
    </body>
</html>
Copy the code

Create demo.js. Loading WebAssembly module has a variety of ways, but the most effective way is through the use of WebAssembly instantiateStreaming function compiled in the form of flow and instantiation. Note that if an assertion fails, we need to provide abort.

(async () => {
    const importObject = {
        env: {
            abort(_msg, _file, line, column) {
                console.error("abort called at index.ts:" + line + ":"+ column); }}}; const module = await WebAssembly.instantiateStreaming( fetch("build/optimized.wasm"),
        importObject
    );
    const isPrime = module.instance.exports.isPrime;

    const result = document.querySelector("#result");
    document.querySelector("#prime-checker").addEventListener("submit", event => {
        event.preventDefault();
        result.innerText = "";
        const number = event.target.elements.number.value;
        result.innerText = `${number} is ${isPrime(number) ? '' : 'not '}prime.`; }); }) ();Copy the code

Now install static-server. Because you want to use WebAssembly instantiateStreaming, we need to create a service, the module you need to use the MIME type of application/wasm.

npm install --save-dev static-server
Copy the code

Add the script to package.json.

{
  "scripts": {
    "serve-demo": "static-server"}}Copy the code

Run NPM run serve-demo and open the localhost URL in your browser. Submit the number in the form and you will receive a message indicating whether the number is prime. We’ve now implemented the entire process from coding in AssemblyScript to actually using it on your website.

conclusion

WebAssembly, and extensions through AssemblyScript, won’t magically make every site faster, but that doesn’t matter. WebAssembly is exciting because it enables more applications to be available on the Web.

Similarly, AssemblyScript makes WebAssembly available to more developers, which makes it easy to default to JavaScript, but use WebAssembly when a lot of computing is required.

Welcome to pay attention to the front end public number: front end pioneer, free to receive the whole series of Webpack tutorials.