Running Go CLI programs in the browser

栏目: IT技术 · 发布时间: 6年前

内容简介:Turns out it’s almost shockingly easy to run Go CLI programs in the browser with WebAssembly (WASM); as an example I’ll use myThe resulting binary is rather large (5.1M); TinyGo can be used to create smaller builds, but itWe then need to load the

Turns out it’s almost shockingly easy to run Go CLI programs in the browser with WebAssembly (WASM); as an example I’ll use my uni program. Building is as easy as:

GOOS=js GOARCH=wasm go build -o wasm/main.wasm

The resulting binary is rather large (5.1M); TinyGo can be used to create smaller builds, but it doesn’t support os.Args yet , so it won’t work here. After gzip compression it’s only 1.3M, so that’s manageable (and still smaller than many “text-only” websites).

We then need to load the main.wasm binary:

<html>
<head>
    <meta charset="utf-8">
</head>
<body>
    <script src="wasm_exec.js"></script>
    <script>
        const go = new Go();
        WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject).then((result) => {
            go.run(result.instance);
        });
    </script>
</body>
</html>

Copy the wasm_exec.js file from the Go source repo ( $(go env GOROOT)/misc/wasm/wasm_exec.js ), or GitHub .

You can’t load the HTML file the local filesystem as the browser will refuse to load the wasm file; you’ll have to use a webserver which serves wasm files with the correct MIME type, for example with Python:

#!/usr/bin/env python3
import http.server
h = http.server.SimpleHTTPRequestHandler
h.extensions_map = {'': 'text/html', '.wasm': 'application/wasm', '.js': 'application/javascript'}
http.server.HTTPServer(('127.0.0.1', 2000), h).serve_forever()

Going to http://localhost:2000 will fetch the file and run uni ; the JS console should display:

uni: no command given
exit code 1

As if we typed uni on the CLI. To give it some arguments set go.argv :

<script>
        const go = new Go();
        WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject).then((result) => {
            // Remember that argv[0] is the program name.
            go.argv = ['uni', '-q', 'identify', 'wasm'];
            go.run(result.instance);
        });
</script>

Which will give the expected output in the console:

'w'  U+0077  119    77          w     LATIN SMALL LETTER W (Lowercase_Letter)
'a'  U+0061  97     61          a     LATIN SMALL LETTER A (Lowercase_Letter)
's'  U+0073  115    73          s     LATIN SMALL LETTER S (Lowercase_Letter)
'm'  U+006D  109    6d          m     LATIN SMALL LETTER M (Lowercase_Letter)

Now it’s a simple matter of connecting an input element to go.argv ; this also fetches the main.wasm just once and re-runs it, instead of re-fetching it every time:

<input id="input">
<script src="wasm_exec.js"></script>
<script>
	fetch('main.wasm').then(response => response.arrayBuffer()).then(function(bin) {
			input.addEventListener('keydown', function(e) {
				if (e.keyCode !== 13)  // Enter
					return;

				e.preventDefault();

				const go = new Go();
				go.argv = ['uni'].concat(this.value.split(' '));
				this.value = '';
				WebAssembly.instantiate(bin, go.importObject).then((result) => {
					go.run(result.instance);
				});
			});
        });
</script>

Overwrite the global.fs.writeSync from wasm_exec.js to display the output in the HTML page instead of the console:

<script>
	fetch('main.wasm').then(response => response.arrayBuffer()).then(function(bin) {
			input.addEventListener('keydown', function(e) {
				if (e.keyCode !== 13)  // Enter
					return;

				e.preventDefault();

				const go = new Go();
				go.argv = ['uni'].concat(this.value.split(' '));
				this.value = '';

				// Write stdout to terminal.
				let outputBuf = '';
				const decoder = new TextDecoder("utf-8");
				global.fs.writeSync = function(fd, buf) {
					outputBuf += decoder.decode(buf);
					const nl = outputBuf.lastIndexOf("\n");
					if (nl != -1) {
						window.output.innerText += outputBuf.substr(0, nl + 1);
						window.scrollTo(0, document.body.scrollHeight);
						outputBuf = outputBuf.substr(nl + 1);
					}
					return buf.length;
				};

				WebAssembly.instantiate(bin, go.importObject).then((result) => {
					go.run(result.instance);
				});
			});
        });
</script>

And that’s pretty much it; 30 lines of JavaScript to run CLI applications in the browser :-) The only change I had to make to uni Go code was adding a build tag .

There are plenty of other things that can be improved: some better styling, reading from stdin, keybinds, loading indicator, etc. The full version does some of that. Take a look at index.html and term.js in case you’re interested. It could still be improved further, but I thought this was “good enough” for a basic demo :-)


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

一本书读懂24种互联网思维

一本书读懂24种互联网思维

安杰 / 台海出版社 / 2015-3-1 / 39.80元

互联网思维已经不再局限于互联网,与当初人类史上的“文艺复兴”一样,这种思维的核心即将开始扩散开去,对整个大时代造成深远的影响。本书是深入研究互联网思维的精华之作,作者深入浅出地集中阐述了24种互联网思维的内核与精神,并结合实例对这24种互联网思维逐一进行了点评。对于个人与企业如何抓住互联网思维背后正喷薄而出的工作、生活、商业上的大革新与大机遇,如何在互联网思维下进行运作,如何运用互联网思维进行升级......一起来看看 《一本书读懂24种互联网思维》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

SHA 加密
SHA 加密

SHA 加密工具