21

AssemblyScript 入门指南[每日前端夜话0xEB]

 4 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzI3NzIzMDY0NA%3D%3D&%3Bmid=2247487735&%3Bidx=1&%3Bsn=ab73bc36de643f292f18f54ed7189ce8
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

每日前端夜话 0xEB

每日前端夜话,陪你聊前端。

每天晚上18:00准时推送。

正文共:2459 字

预计阅读时间:10 分钟

作者:Danny Guo

翻译:疯狂的技术宅

来源: logrocket

ZJ32Mz6.jpg!web

WebAssembly【 https://webassembly.org/ 】(Wasm)是 Web 浏览器中相对较新的功能,但它地扩展了把 Web 作为服务应用平台的功能潜力。

对于 Web 开发人员来说,学习使用 WebAssembly 可能会有一个艰难的过程,但是 AssemblyScript【 https://assemblyscript.org/ 】 提供了一种解决方法。首先让我们看一下为什么 WebAssembly 是一项很有前途的技术,然后再看怎样 AssemblyScript 挖掘潜力。

WebAssembly

WebAssembly 是浏览器的低级语言,为开发人员提供了除 JavaScript 之外的 Web 编译目标。它使网站代码可以在安全的沙盒环境中以接近本机的速度运行。

它是根据所有主流浏览器(Chrome,Firefox,Safari 和 Edge)所代表的意见开发的,他们达成了设计共识【 https://lists.w3.org/Archives/Public/public-webassembly/2017Feb/0002.html 】,这些浏览器现在都支持 WebAssembly。

WebAssembly 以二进制格式交付,这意味着与 JavaScript 相比,WebAssembly 在大小和加载时间上都具有优势。但是它也有易于理解的文本表示形式【 https://developer.mozilla.org/zh-CN/docs/WebAssembly/Understanding_the_text_format 】。

当 WebAssembly 首次发布时,一些开发人员认为它有可能最终取代 JavaScript 作为 Web 的主要语言。但是最好把 WebAssembly 看作是与现有 Web 平台良好集成的新工具,这是它的高级目标【 https://webassembly.org/docs/high-level-goals/ 】。

WebAssembly 并没有取代 JavaScript 现有的用例,而是吸引了更多人,因为它引入了新的用例。目前 WebAssembly 还不能直接访问 DOM,大多数网站都希望使用 JavaScript,经过多年的优化,JavaScript 已经相当快了。以下 WebAssembly 可能的使用案例列表【 https://webassembly.org/docs/use-cases/ 】的示例:

  • 游戏

  • 科学的可视化和模拟

  • CAD应用

  • 图像/视频编辑

这些应用共同特点是,它们通常会被看作是桌面应用。通过为 CPU 密集型任务提供接近本机的性能,WebAssembly 使得将这些程序迁移至 Web 成为可行。

现有网站也可以从 WebAssembly 中受益。Figma【 https://www.figma.com/ 】提供了一个真实的例子,它通过使用 WebAssembly 大大缩短了其加载时间。如果网站使用进行大量计算的代码,则可以将其替换为 WebAssembly 以提高性能。

也许现在你对怎样使用 WebAssembly 感兴趣。你可以学习语言本身并直接编写【 https://blog.scottlogic.com/2018/04/26/webassembly-by-hand.html 】 ,但实际上它打算成为其他语言的编译目标【 https://github.com/appcypher/awesome-wasm-langs 】 。它被设计【 https://webassembly.org/docs/c-and-c++/ 】为对 C 和 C++ 具有良好的支持,Go语言在 version 1.11 中增加了实验性支持的版本中,Rust 也对其进行了大量投入。

但是也许你并不想为了使用 WebAssembly 而学习或使用其中某种语言。这就是 AssemblyScript 存在的意义。

AssemblyScript

AssemblyScript 是一个把 TypeScript 转换到 WebAssembly 的编译器。由微软开发的 TypeScript 将类型添加到了 JavaScript 中。它已经变得相当受欢迎【 https://insights.stackoverflow.com/survey/2019#most-popular-technologies 】,即使对于不熟悉它的人,AssemblyScript 只允许 TypeScript 的有限功能子集,因此不需要花太多时间就可以上手。。

因为它与 JavaScript 非常相似,所以 AssemblyScript 使 Web 开发人员可以轻松地将 WebAssembly 整合到他们的网站中,而不必使用完全不同的语言。

试用

让我们编写第一个 AssemblyScript 模块(以下所有代码均可在 GitHub 上【 https://github.com/dguo/assemblyscript-demo 】找到)。我们需要 Node.js 的最低版本为 8 才能得到 WebAssembly 的支持。

转到一个空目录,创建一个 package.json 文件,然后安装 AssemblyScript。请注意,我们需要直接从它的 GitHub 存储库【 https://github.com/AssemblyScript/assemblyscript 】安装。它尚未在 npm 上发布,因为 AssemblyScript 开发人员还没有考虑编译器是否已经准备好能够支持广泛使用。

1mkdir assemblyscript-demo
2cd assemblyscript-demo
3npm init
4npm install --save-dev github:AssemblyScript/assemblyscript

使用 asinit 命令生成脚手架文件:

1npx asinit .

我们的 package.json 现在应该包含以下脚本:

1{
2  "scripts": {
3    "asbuild:untouched": "asc assembly/index.ts -b build/untouched.wasm -t build/untouched.wat --sourceMap --validate --debug",
4    "asbuild:optimized": "asc assembly/index.ts -b build/optimized.wasm -t build/optimized.wat --sourceMap --validate --optimize",
5    "asbuild": "npm run asbuild:untouched && npm run asbuild:optimized"
6  }
7}

顶层的 index.js 看起来像这样:

 1const fs = require("fs");
 2const compiled = new WebAssembly.Module(fs.readFileSync(__dirname + "/build/optimized.wasm"));
 3const imports = {
 4  env: {
 5    abort(_msg, _file, line, column) {
 6       console.error("abort called at index.ts:" + line + ":" + column);
 7    }
 8  }
 9};
10Object.defineProperty(module, "exports", {
11  get: () => new WebAssembly.Instance(compiled, imports).exports
12});

它使我们能够像使用普通的 JavaScript 模块一样轻松地 require WebAssembly 模块。

assembly 目录中包含我们的 AssemblyScript 源代码。生成的示例是一个简单的加法函数。

1export function add(a: i32, b: i32): i32 {
2  return a + b;
3}

函数签名就像在 TypeScript 中那样,它之所以使用 i32 的原因是 AssemblyScript 使用了 WebAssembly 的特定整数和浮点类型【 https://docs.assemblyscript.org/basics/types 】,而不是 TypeScript 的通用 number 类型【。

让我们来构建示例。

1npm run asbuild

build 目录现在应包含以下文件:

1optimized.wasm
2optimized.wasm.map
3optimized.wat
4untouched.wasm
5untouched.wasm.map
6untouched.wat

我们得到了构建的普通版本和优化版本。对于每个构建版本,都有一个 .wasm 二进制文件,一个 .wasm.map 源码映射【 https://developer.mozilla.org/zh-CN/docs/Tools/Debugger/How_to/Use_a_source_map 】,以及二进制文件的 .wat 文本表示形式。文本表示形式是为了供人阅读,但现在我们无需阅读或理解它——使用 AssemblyScript 的目的之一就是我们不需要使用原始 WebAssembly。

启动 Node 并像其他模块一样使用编译模块。

1$ node
2Welcome to Node.js v12.10.0.
3Type ".help" for more information.
4> const add = require('./index').add;
5undefined
6> add(3, 5)
78

这就是从 Node 调用 WebAssembly 所需要的全部!

添加监视脚本

为了便于开发,我建议你在每次更改源代码时都用 onchange【 https://github.com/Qard/onchange 】 自动重建模块,因为 AssemblyScript  尚不包括监视模式【 https://github.com/AssemblyScript/assemblyscript/issues/624 】。

1npm install --save-dev onchange

package.json 中添加一个 asbuild:watch 脚本。包含 -i 标志即可在运行命令后立即运行初始构建。

1{
2  "scripts": {
3    "asbuild:untouched": "asc assembly/index.ts -b build/untouched.wasm -t build/untouched.wat --sourceMap --validate --debug",
4    "asbuild:optimized": "asc assembly/index.ts -b build/optimized.wasm -t build/optimized.wat --sourceMap --validate --optimize",
5    "asbuild": "npm run asbuild:untouched && npm run asbuild:optimized",
6    "asbuild:watch": "onchange -i 'assembly/**/*' -- npm run asbuild"
7  }
8}

现在你可以运行 asbuild:watch ,而不必不断地重新运行 asbuild

性能

让我们写一个基本的基准测试,用来了解究竟可以获得什么样的性能提升。WebAssembly 的专长是处理诸如数字计算之类的 CPU 密集型任务,所以我们用一个函数来确定整数是否为质数。

我们的参考实现如下所示。这是一种幼稚的暴力解决方案,因为我们的目标是执行大量计算。

 1function isPrime(x) {
 2    if (x < 2) {
 3        return false;
 4    }
 5
 6    for (let i = 2; i < x; i++) {
 7        if (x % i === 0) {
 8            return false;
 9        }
10    }
11
12    return true;
13}

等效的 AssemblyScript 版本仅需要一些类型注释:

 1function isPrime(x: u32): bool {
 2    if (x < 2) {
 3        return false;
 4    }
 5
 6    for (let i: u32 = 2; i < x; i++) {
 7        if (x % i === 0) {
 8            return false;
 9        }
10    }
11
12    return true;
13}

我们将使用 Benchmark.js【 https://benchmarkjs.com/ 】。

1npm install --save-dev benchmark

创建 benchmark.js

 1const Benchmark = require('benchmark');
 2
 3const assemblyScriptIsPrime = require('./index').isPrime;
 4
 5function isPrime(x) {
 6    for (let i = 2; i < x; i++) {
 7        if (x % i === 0) {
 8            return false;
 9        }
10    }
11
12    return true;
13}
14
15const suite = new Benchmark.Suite;
16const startNumber = 2;
17const stopNumber = 10000;
18
19suite.add('AssemblyScript isPrime', function () {
20    for (let i = startNumber; i < stopNumber; i++) {
21        assemblyScriptIsPrime(i);
22    }
23}).add('JavaScript isPrime', function () {
24    for (let i = startNumber; i < stopNumber; i++) {
25        isPrime(i);
26    }
27}).on('cycle', function (event) {
28    console.log(String(event.target));
29}).on('complete', function () {
30    const fastest = this.filter('fastest');
31    const slowest = this.filter('slowest');
32    const difference = (fastest.map('hz') - slowest.map('hz')) / slowest.map('hz') * 100;
33    console.log(`${fastest.map('name')} is ~${difference.toFixed(1)}% faster.`);
34}).run();

在我的机器上,运行 node benchmark 时得到了以下结果:

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

请注意,这个测试是一个 microbenchmark【 https://stackoverflow.com/a/2842707/1481479 】,我们应该谨慎阅读。

对于一些更多的 AssemblyScript 基准测试,我建议你查看 WasmBoy 基准测试【 https://wasmboy.app/benchmark/ 】 和波动方程式基准测试【 https://jtiscione.github.io/webassembly-wave/index.html 】。

加载模块

接下来,在网站中使用我们的模块。

先创建 index.html

 1<!DOCTYPE html>
 2<html>
 3    <head>
 4        <meta charset="utf-8" />
 5        <title>AssemblyScript isPrime demo</title>
 6    </head>
 7    <body>
 8        <form id="prime-checker">
 9            <label for="number">Enter a number to check if it is prime:</label>
10            <input name="number" type="number" />
11            <button type="submit">Submit</button>
12        </form>
13
14        <p id="result"></p>
15
16        <script src="demo.js"></script>
17    </body>
18</html>

再创建 demo.js 。加载 WebAssembly 模块有多种方式【 https://developers.google.com/web/updates/2018/04/loading-wasm 】,但是最有效的方法是通过使用 WebAssembly.instantiateStreaming 函数以流的方式编译和实例化。请注意,如果 assertion【 https://docs.assemblyscript.org/basics/environment#utility 】 失败的话,我们需要提供 abort 函数【 https://docs.assemblyscript.org/details/debugging#overriding-abort 】。

 1(async () => {
 2    const importObject = {
 3        env: {
 4            abort(_msg, _file, line, column) {
 5                console.error("abort called at index.ts:" + line + ":" + column);
 6            }
 7        }
 8    };
 9    const module = await WebAssembly.instantiateStreaming(
10        fetch("build/optimized.wasm"),
11        importObject
12    );
13    const isPrime = module.instance.exports.isPrime;
14
15    const result = document.querySelector("#result");
16    document.querySelector("#prime-checker").addEventListener("submit", event => {
17        event.preventDefault();
18        result.innerText = "";
19        const number = event.target.elements.number.value;
20        result.innerText = `${number} is ${isPrime(number) ? '' : 'not '}prime.`;
21    });
22})();

现在安装 static-server【 https://github.com/nbluis/static-server 】。因为要使用 WebAssembly.instantiateStreaming ,我们需要创建服务,该模块需要使用 MIME type【 https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types 】 的   application/wasm

1npm install --save-dev static-server

将脚本添加到 package.json 中。

1{
2  "scripts": {
3    "serve-demo": "static-server"
4  }
5}

运行 npm run serve-demo 并在浏览器中打开 localhost URL。提交表单中的数字,你将收到一条消息,指出该数字是否为素数。现在,我们已经实现了从用 AssemblyScript 编码到在网站中实际使用的整个过程。

结论

WebAssembly 以及通过 AssemblyScript 的扩展,不会使每个网站都神奇地变得更快,但是这并不重要。WebAssembly 之所以令人兴奋,是因为它可以使更多的应用在 Web 变得中可行。

类似地,AssemblyScript 使更多开发人员可以使用 WebAssembly,这使我们很容易默认使用 JavaScript,但是当需要大量运算工作时,可以用 WebAssembly。

原文: https://blog.logrocket.com/the-introductory-guide-to-assemblyscript/

下面夹杂一些私货:也许你和高薪之间只差这一张图

2019年京程一灯课程体系上新,这是我们第一次将全部课程列表对外开放。

愿你有个好前程,愿你月薪30K。我们是认真的 ! BbquyaF.png!web

zMFVruu.jpg!web

在公众号内回复“体系”查看高清大图

长按二维码,加大鹏老师微信好友

拉你加入前端技术交流群

唠一唠怎样才能拿高薪

JFNJFbv.jpg!web

小手一抖,资料全有。长按二维码关注 前端先锋 ,阅读更多技术文章和业界动态。

MFryQjN.gif


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK