如何通过 JavaScript 运行用 Go 编写的 WebAssembly 模块? 下

简介: 如何通过 JavaScript 运行用 Go 编写的 WebAssembly 模块?

prettyJson 程序


接下来我们来实现 prettyJson 程序。


实现 prettyJson 函数


prettyJson 程序会将未格式化的 JSON 字符串作为输入参数,对其进行格式化,然后将格式化的 JSON 字符串作为输出返回。我们使用 MarshalIndent 函数来完成这个操作。

将以下代码添加到 ~/go/bin/webassembly/cmd/wasm/main.go 文件中。


func prettyJson(input string) (string, error) {  
    var raw interface{}
    if err := json.Unmarshal([]byte(input), &raw); err != nil {
        return "", err
    }
    pretty, err := json.MarshalIndent(raw, "", "  ")
    if err != nil {
        return "", err
    }
    return string(pretty), nil
}
同时需要导一下包。
go
复制代码
import (
    "encoding/json"
    "fmt"
)

MarshalIndent 函数有 3 个参数。

第一个是未格式化的原始 JSON。

第二个是添加到 JSON 的每个新行的前缀。我们不添加前缀,所以是空字符串。

第三个参数是为 JSON 的每个缩进添加的字符串。我们传递两个空格字符串。

总之这段代码的含义是,对 JSON 每个新的缩进,都会添加两个空格,通过这种方式来完成 JSON 的格式化。

如果传递的 JSON 字符串是这样的:


{"user_id": "571401777717031","user_name": "代码与野兽","description": "关注real-time web、低代码、数据可视化、web3等领域。"}

那么它的输出下面这种被格式化的 JSON 字符串。\


{
  "user_id": "571401777717031",
  "user_name": "代码与野兽",
  "description": "关注real-time web、低代码、数据可视化、web3等领域。"
}


将 Go 中的函数暴露给 Javascript


现在我们已经实现了 prettyJSON 函数,但是我们还没有将这个函数公开给 Javascript,这样 JavaScript 没办法调用它。

Go 提供了syscall/js 包,它可以将函数从 Go 公开到 Javascript。

将 Go 的函数公开到 JavaScript 的第一步是创建一个 Func 类型的变量。

Func 是一个封装好的 Go 函数,可以被 JavaScript 调用。FuncOf 函数可以创建 Func 类型变量。

在 ~/go/bin/webassembly/cmd/wasm/main.go 中添加 jsonWrapper 函数。


func jsonWrapper() js.Func {
  jsonFunc := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
    if len(args) != 1 {
      return "Invalid no of arguments passed"
    }
    inputJSON := args[0].String()
    fmt.Printf("input %s\n", inputJSON)
    pretty, err := prettyJson(inputJSON)
    if err != nil {
      fmt.Printf("unable to convert to json %s\n", err)
      return err.Error()
    }
    return pretty
  })
  return jsonFunc
}

注意需要导包。


import (
    "encoding/json"
    "fmt"
    "syscall/js"
)

FuncOf 函数具有两个参数。

第一个参数是 Javascript 的 this 关键字。this 指向的是 JavaScript 的 global 对象,在浏览器中就是 window 对象。

第二个参数是一个切片,[]js.Value 表示在调用 Javascript 函数时传递的参数。在我们的例子中,它是未格式化的 JSON 输入字符串。

我们首先检查在 Javascript 中是否只传递了一个参数。这个检查是必要的,因为我们只需要一个 JSON 字符串参数。如果不是,我们返回一个字符串消息。

我们使用 args[0].String() 来将参数转换为字符串类型。

获取到输入的 JSON 后,我在第 8 行调用 prettyJson 函数完成美化工作,并返回输出结果。

当从 Go 向 Javascript 返回值时,编译器会自动使用 ValueOf 函数将 Go 中的值转换为 JavaScript 中的值。我们从 Go 中返回一个 string,它被 js.ValueOf() 编译器转换为相应的 JavaScript 字符串类型的值。

我们将 FuncOf 的返回值赋值给 jsonFunc。

现在 jsonFunc 是一个可以被 Javascript 调用的函数。

在最后我们把 jsonFunc 函数返回了出去。

现在我们已经准备好可以从 Javascript 调用的函数了,还差最后一步。

我们需要公开我们刚刚创建的函数,来让 Javascript 调用它。

将 Go 函数暴露给 Javascript 的方法是将 JavaScript 的全局对象的 prettyJSON 属性设置为 jsonWrapper()。

对应的代码如下:


js.Global().Set("prettyJSON", jsonWrapper())

把这行代码添加到 main() 函数的末尾。

在上面的代码中,我们将 Javascript 的 Global 对象的 formatJSON 属性设置为 jsonWrapper 函数的返回值。

现在 jsonFunc 可以使用函数名 prettyJSON 从 JavaScript 调用。

下面是完整的代码:


package main
import (
  "encoding/json"
  "fmt"
  "syscall/js"
)
func jsonWrapper() js.Func {
  jsonFunc := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
    if len(args) != 1 {
      return "Invalid no of arguments passed"
    }
    inputJSON := args[0].String()
    fmt.Printf("input %s\n", inputJSON)
    pretty, err := prettyJson(inputJSON)
    if err != nil {
      fmt.Printf("unable to convert to json %s\n", err)
      return err.Error()
    }
    return pretty
  })
  return jsonFunc
}
func prettyJson(input string) (string, error) {
  var raw interface{}
  if err := json.Unmarshal([]byte(input), &raw); err != nil {
    return "", err
  }
  pretty, err := json.MarshalIndent(raw, "", "  ")
  if err != nil {
    return "", err
  }
  return string(pretty), nil
}
func main() {
  fmt.Println("Go Web Assembly")
  js.Global().Set("prettyJSON", jsonWrapper())
}

现在我们来编译和测试刚刚完成的程序。


cd ~/go/bin/webassembly/cmd/wasm/  
GOOS=js GOARCH=wasm go build -o  ../../assets/json.wasm  
cd ~/go/bin/webassembly/cmd/server/  
go run main.go

上面的命令会编译 wasm 二进制文件并启动 Web 服务器。


从 JavaScript 调用 Go 函数


我们已经成功地将 Go 函数暴露给 JavaScript,现在我们来检查它是否有效。

在浏览器中打开 http://localhost:9090/,然后打开 Javascript 控制台。

在 Javascript 控制台中调用 prettyJSON 函数:


prettyJSON('{"user_id": "571401777717031","user_name": "代码与野兽","description": "关注real-time web、低代码、数据可视化、web3等领域。"}')

这时我们得到了一个错误:Error: Go program has already exited

image.png

错误的原因是我们的 Go 程序在当 Javascript 调用时已经退出了。

该如何解决这个问题呢?

其实很简单,我们只需要保证 Go 程序在 JavaScript 调用它时仍然处于运行状态。

在 Go 中实现这个需求最简单的方法就是通过 channel 来让程序一直等待。


func main() {  
        fmt.Println("Go Web Assembly")
        js.Global().Set("formatJSON", jsonWrapper())
        <-make(chan bool)
}

重新编译程序,然后再次尝试在浏览器中调用 prettyJSON 函数。


prettyJSON('{"user_id": "571401777717031","user_name": "代码与野兽","description": "关注real-time web、低代码、数据可视化、web3等领域。"}')

现在 JSON 将会被美化。

image.png

当我们没有传入参数时,也会有正常的错误输出:

image.png

OK,通过本文的讲解,相信你已经成功地通过 JavaScript 调用了一个使用 Go 编写的函数。



相关文章
|
8天前
|
数据挖掘 API Go
《Go 简易速速上手小册》第7章:包管理与模块(2024 最新版)(下)
《Go 简易速速上手小册》第7章:包管理与模块(2024 最新版)
38 1
|
8天前
|
数据采集 JavaScript 前端开发
使用Go和JavaScript爬取股吧动态信息的完整指南
本文介绍了如何使用Go和JavaScript构建网络爬虫,从股吧网站抓取实时股市信息。通过设置代理服务器以应对反爬策略,利用`got`库执行JavaScript提取动态数据,如用户讨论和市场分析。示例代码展示了爬虫的实现过程,包括浏览器实例创建、代理配置、JavaScript执行及数据打印。此方法有助于投资者及时获取市场资讯,为决策提供支持。
使用Go和JavaScript爬取股吧动态信息的完整指南
|
8天前
|
移动开发 资源调度 前端开发
nbcio-vue下载安装后运行报错,diagram-js没有安装
nbcio-vue下载安装后运行报错,diagram-js没有安装
13 0
|
8天前
|
存储 Java Linux
聊聊Go程序是如何运行的
本文作者 **sharkChili** 是一名 Java 和 Go 语言开发者,同时也是 CSDN 博客专家和 JavaGuide 维护者。文章探讨了 Go 语言的执行过程,从汇编角度出发,解释了如何从 `main.go` 文件开始,经过入口跳转、参数拷贝、启动协程、运行 `g0` 的 `main` 方法等步骤,最终执行到用户定义的 `main` 函数。文章还展示了相关汇编代码片段,并提供了运行时检查、系统初始化和调度器初始化的细节。结尾提到,有兴趣的读者可以加入作者创建的交流群进行深入讨论。
14 0
|
8天前
|
JavaScript 前端开发 测试技术
编写JavaScript模块化代码主要涉及将代码分割成不同的文件或模块,每个模块负责处理特定的功能或任务
【5月更文挑战第10天】编写JavaScript模块化代码最佳实践:使用ES6模块或CommonJS(Node.js),组织逻辑相关模块,避免全局变量,封装细节。利用命名空间和目录结构,借助Webpack处理浏览器环境的模块。编写文档和注释,编写单元测试以确保代码质量。通过这些方法提升代码的可读性和可维护性。
20 3
|
8天前
|
消息中间件 监控 JavaScript
Node.js中的进程管理:child_process模块与进程管理
【4月更文挑战第30天】Node.js的`child_process`模块用于创建子进程,支持执行系统命令、运行脚本和进程间通信。主要方法包括:`exec`(执行命令,适合简单任务)、`execFile`(安全执行文件)、`spawn`(实时通信,处理大量数据)和`fork`(创建Node.js子进程,支持IPC)。有效的进程管理策略涉及限制并发进程、处理错误和退出事件、使用流通信、谨慎使用IPC以及监控和日志记录,以确保应用的稳定性和性能。
|
8天前
|
安全 Go API
【Go 语言专栏】Go 语言的模块版本控制与管理
【4月更文挑战第30天】Go 语言模块版本控制始于 1.11 版本,提供了一种替代 GOPATH 的更灵活的依赖管理方式。语义化版本号(主、次、修订版本号)用于标识模块变化和兼容性。开发中可采取固定、范围或最新版本策略。`go mod`工具用于管理模块,升级时注意兼容性、测试和文档更新。实践案例展示如何有效控制与管理模块版本,确保项目稳定、兼容和可维护。随着 Go 语言的发展,模块版本管理将持续优化。
|
8天前
|
编解码 JavaScript 前端开发
【专栏】介绍了字符串Base64编解码的基本原理和在Java、Python、C++、JavaScript及Go等编程语言中的实现示例
【4月更文挑战第29天】本文介绍了字符串Base64编解码的基本原理和在Java、Python、C++、JavaScript及Go等编程语言中的实现示例。Base64编码将24位二进制数据转换为32位可打印字符,用“=”作填充。文中展示了各语言的编码解码代码,帮助开发者理解并应用于实际项目。
|
8天前
|
缓存 JavaScript 前端开发
Node.js的模块系统:CommonJS模块系统的使用
【4月更文挑战第29天】Node.js采用CommonJS作为模块系统,每个文件视为独立模块,通过`module.exports`导出和`require`引入实现依赖。模块有独立作用域,保证封装性,防止命名冲突。引入的模块会被缓存,提高加载效率并确保一致性。利用CommonJS,开发者能编写更模块化、可维护的代码。
|
8天前
|
缓存 NoSQL Go
《Go 简易速速上手小册》第7章:包管理与模块(2024 最新版)(上)
《Go 简易速速上手小册》第7章:包管理与模块(2024 最新版)
71 1