如何通过 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 编写的函数。



相关文章
|
12月前
|
JavaScript 前端开发
在Node.js中,如何合理使用模块来避免全局变量的问题?
在Node.js中,如何合理使用模块来避免全局变量的问题?
591 167
|
JavaScript 前端开发 Java
通义灵码 Rules 库合集来了,覆盖Java、TypeScript、Python、Go、JavaScript 等
通义灵码新上的外挂 Project Rules 获得了开发者的一致好评:最小成本适配我的开发风格、相当把团队经验沉淀下来,是个很好功能……
1917 103
|
7月前
|
Java 编译器 Go
【Golang】(1)Go的运行流程步骤与包的概念
初次上手Go语言!先来了解它的运行流程吧! 在Go中对包的概念又有怎样不同的见解呢?
355 4
|
12月前
|
Go 持续交付 开发者
Go语言包与模块(module)的基本使用-《Go语言实战指南》
本章深入讲解Go语言中的包(Package)和模块(Module)概念。包是代码组织的最小单位,每个`.go`文件属于一个包,通过`import`实现复用;主程序包需命名为`main`。模块是Go 1.11引入的依赖管理机制,支持自动版本管理和私有/远程仓库,无需依赖GOPATH。通过实际示例,如自定义包`mathutil`和第三方模块`gin`的引入,展示其使用方法。常用命令包括`go mod init`、`go mod tidy`等,帮助开发者高效管理项目依赖。最后总结,包负责功能划分,模块实现现代化依赖管理,提升团队协作效率。
457 15
|
12月前
|
Go 调度
GO语言函数的内部运行机制分析
以上就是Go语言中函数的内部运行机制的概述,展示了函数在Go语言编程中如何发挥作用,以及Go如何使用简洁高效的设计,使得代码更简单,更有逻辑性,更易于理解和维护。尽管这些内容深入了一些底层的概念,但我希望通过这种方式,将这些理论知识更生动、更形象地带给你,让你在理解的同时找到编程的乐趣。
221 5
|
机器学习/深度学习 自然语言处理 前端开发
前端神经网络入门:Brain.js - 详细介绍和对比不同的实现 - CNN、RNN、DNN、FFNN -无需准备环境打开浏览器即可测试运行-支持WebGPU加速
本文介绍了如何使用 JavaScript 神经网络库 **Brain.js** 实现不同类型的神经网络,包括前馈神经网络(FFNN)、深度神经网络(DNN)和循环神经网络(RNN)。通过简单的示例和代码,帮助前端开发者快速入门并理解神经网络的基本概念。文章还对比了各类神经网络的特点和适用场景,并简要介绍了卷积神经网络(CNN)的替代方案。
2130 1
|
缓存 JSON JavaScript
Node.js模块系统
10月更文挑战第4天
162 2
|
前端开发 JavaScript Java
JavaScript的运行原理
JavaScript 的运行原理包括代码输入、解析、编译、执行、内存管理和与浏览器交互几个步骤。当打开网页时,浏览器加载 HTML、CSS 和 JavaScript 文件,并通过 JavaScript 引擎将其解析为抽象语法树(AST)。接着,引擎将 AST 编译成字节码或机器码,并在执行阶段利用事件循环机制处理异步操作,确保单线程的 JavaScript 能够高效运行。同时,JavaScript 引擎还负责内存管理和垃圾回收,以减少内存泄漏。通过与 DOM 的交互,JavaScript 实现了动态网页效果,提供了灵活且高效的开发体验。
|
缓存 JavaScript 前端开发
JavaScript模块化开发:ES6模块与CommonJs的对比与应用
JavaScript模块化开发:ES6模块与CommonJs的对比与应用
382 2
|
JavaScript 应用服务中间件 Apache
Node.js Web 模块
10月更文挑战第7天
172 0