如何在JavaScript中使用Golang库?

如何在JavaScript中使用Golang库? 大家好!

我正在我的项目中使用一个Go开源库。

我们的整个项目都是用JS编写的,所以我希望能在某些JS代码中使用整个Go库。

有没有人知道并推荐一些在JS中或与JS一起使用Go的合适(高效)方法?

提前感谢,

Hoodie

10 回复

这是一篇非常有用的文章。感谢分享!

更多关于如何在JavaScript中使用Golang库?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你好 Sibert,

我希望能够更深入地使用 Go 库,例如在 JS 中使用它的函数或文件本身。

不过无论如何,还是谢谢你!

你好,安迪

我想在同一台Mac机器上使用NodeJS来运行一个Go库。 所以我正在尝试检查GopherJS和这个Go库的兼容性!

例如,在JS中使用其函数或文件本身

我也在寻找将Go与Javascript结合使用的方法。但我发现,除了某种微服务之外,没有其他方法可以在解释型语言和编译型语言之间进行通信(它们不使用相同的引擎)。也就是说来回发送消息。如果能证明我是错的,我会很高兴。

引用 hoodie: 有没有一种合适(高效)的方式在 JavaScript 中使用 Go 或与 JavaScript 一起使用?

一种方法是使用 JavaScript 的 fetch() 与具有端点的 Go HTTP 服务器进行通信。

以下代码通过使用 innerHTML 将期望的 Go 输出写入网页:

function ask_go() {
  let url = "/askgo"
  fetch(url)
    .then((res) => res.text())
    .then((response) => {
      document.getElementById("content").innerHTML = response;
    })
    .catch((error) => alert("Error:", error));
}

正如你所说,我也发现了那些选项,以及将Go包编译成C共享库的方法(从其他语言调用Go函数 | 作者:Vladimir Vivien | Learning the Go Programming Language | Medium)!

因此,如果不存在其他更好的选择,那么深入研究其中一种(或几种)方法会更好!

谢谢大家!微笑

我猜您担心的是性能会受到影响——但也许最好的办法是对Go HTTP服务器进行基准测试,看看性能如何,因为开销可能微不足道——这实际上取决于您调用Go函数的频率以及它们完成的工作量。

您可能还会发现,可以同时发送多个请求,这可能会带来好处。或者将一组函数调用转换为单个“服务”。

我还建议您了解一下TinyGo——它只比完整的Go稍差一点——但对于Wasm来说似乎更好——然后您可以直接从Node.js中使用它——但是,如果您使用Wasm,使用编译后的Go将不会获得任何性能优势。

在不了解您正在使用的库以及使用频率和用途的情况下,这仍然不确定——基准测试应该会有所帮助。

能否请您更详细地描述一下您期望的架构——例如,您是从浏览器使用JavaScript还是NodeJS。您是否希望在同一台机器上运行Go?您使用的是移动设备、桌面设备还是其他设备?什么操作系统(不过应该无关紧要)?

例如,我使用浏览器JavaScript配合Go HTTP服务器(Windows),通过REST进行通信,同时也使用WebSocket实现服务器推送。为此,我使用标准的Go net/http 配合mux和websocket。我还使用getlantern/systray来创建一个图标,在服务器运行后(例如在我的编辑器中)可以使用该图标通过lorca打开Chrome。请注意,我最多只需要60帧/秒的更新,这可能不符合您的情况……

祝好——安迪

你是否已经研究过:

  • GopherJS(即将Go编译为JS),或者
  • 将Go编译为WASM?

在没有深入研究这些选项的情况下,我认为这些可能是从JS调用Go库的合适方法。

编辑补充:

GopherJS 允许从JS调用Go代码。

WebAssembly 允许从JavaScript调用任何编译为WASM(WebAssembly模块)的代码。

TinyGo 特别适合用于WASM,因为它具有更小的运行时占用空间。(关于从JS调用WASM库的文章。)


旁注:Go项目 wazero 则走向了另一个方向:从Go调用WASM。

(给自己提个醒:我该开始好好理解一下WebAssembly了。)

一种常见的方式是将Go库编译为WebAssembly,然后在JavaScript中调用。以下是具体步骤:

  1. 编写Go代码并导出函数
// main.go
package main

import "syscall/js"

func add(a, b int) int {
    return a + b
}

func main() {
    c := make(chan struct{}, 0)
    js.Global().Set("goAdd", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
        return add(args[0].Int(), args[1].Int())
    }))
    <-c
}
  1. 编译为WebAssembly
GOOS=js GOARCH=wasm go build -o main.wasm main.go
  1. 在HTML中加载
<!DOCTYPE html>
<script src="wasm_exec.js"></script>
<script>
    const go = new Go();
    WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject)
        .then((result) => {
            go.run(result.instance);
            // 调用Go函数
            console.log(goAdd(5, 3)); // 输出8
        });
</script>

另一种方式是通过子进程调用编译后的Go二进制文件:

// Node.js示例
const { spawn } = require('child_process');

const goProcess = spawn('./your-go-binary', ['arg1', 'arg2']);

goProcess.stdout.on('data', (data) => {
    console.log(`输出: ${data}`);
});

goProcess.stderr.on('data', (data) => {
    console.error(`错误: ${data}`);
});

对于需要高性能的场景,可以考虑使用gRPC-Web:

  1. Go服务端
// server.go
package main

import (
    "net/http"
    "github.com/improbable-eng/grpc-web/go/grpcweb"
    "google.golang.org/grpc"
)

func main() {
    grpcServer := grpc.NewServer()
    wrappedServer := grpcweb.WrapServer(grpcServer)
    http.ListenAndServe(":8080", wrappedServer)
}
  1. JavaScript客户端
// client.js
import { grpc } from "@improbable-eng/grpc-web";

const client = new YourServiceClient("http://localhost:8080");
const request = new YourRequest();
client.yourMethod(request, (err, response) => {
    if (err) console.error(err);
    console.log(response);
});

如果Go库提供了C接口,也可以使用node-ffi:

const ffi = require('ffi-napi');

const lib = ffi.Library('./libyourgo.so', {
    'GoFunction': ['int', ['int', 'int']]
});

const result = lib.GoFunction(5, 3);
console.log(result); // 8

这些方法各有适用场景:WebAssembly适合浏览器环境,子进程调用适合Node.js本地执行,gRPC-Web适合网络服务,FFI适合系统级集成。

回到顶部