AOS 2.0 都有哪些让人兴奋的更新(附 Demo)

  • PermaDAO
  • 更新于 2024-08-28 18:19
  • 阅读 101

AOS 2.0 发布更新已经一周时间了。这次更新可谓是研发者的福音,尤其是其中的消息等待机制,让开发者不用再陷入繁杂的异步调用网络中。那么接下来让我们一睹为快吧!

1.jpg


作者: txohyeah

审阅:outprog

来源:内容公会 - 新闻


AOS 升级

如果你需要新创建一个 aos 2.0 的进程,那么需要更新本地的 aos 包,命令如下

npm i -g https://preview_ao.g8way.io

如果是过去已经创建的进程,那么需要在更新本地的 aos 到 2.0 以后,再连接上进程后,运行.update命令。如下图,升级完成后,aos 给出的 prompt 有如下变化,就是升级成功了。

2.png

本次升级最主要包含 3 个功能的升级:

  1. Receive() 方法,类似于 javascript 中的 await 方法。
  2. 通过更符合语义的方式,对消息进行 Request 和 Response。
  3. 对于 Handlers 中的匹配给出了更加符合语义的语法。

Receive() 方法

案例

简单的说这个方法就是 aos 中的 await 方法。这也是官方的解释:the await of aos.

相信这样解释各位开发人员已经能很好理解了,我也不做过多解释。直接看个例子:

aos2.0@aos-2.0.0.rc2[Inbox:2]> .editor
<editor mode> use '.done' to submit or '.cancel' to cancel
Send({Target = LlamaCoinProcessId, Action = "Balance", Tags = { Target = ao.id }})
local res = Receive({From = LlamaCoinProcessId})
print(res.Data)
.done

=========== print result ==============
0

上面简单的三行代码,就可以避免之前把逻辑散落各处的 Handler 的写法。可以让开发者逻辑更清晰,代码可以被更好的阅读。

原理介绍

  1. Receive(pattern) 是一个内置的全局方法。Receive 函数中使用了新引入的特性 coroutine,Lua 中的 coroutine 是一种实现协同程序(协程)的机制。协程是一种比线程更轻量级的执行单元,可以在用户空间调度和管理,不需要操作系统级别的支持。在代码运行的当前位置进行 yield(即阻塞本次执行,让渡出资源,等到接收到匹配的消息,再恢复执行),如上面例子中就是 local res = Receive({From = LlamaCoinProcessId}) 这句。然后通过 Handlers.once() 函数,添加一个一次性的 Handlers 去监听下一次满足 pattern 的消息。当监听到该消息后,resume 这里的代码并继续运行。

PS:Handlers.once() 也是 aos2.0 的新特性,后面会介绍到。

  1. pattern 是匹配模式,相当于之前添加 Handlers 时的第二个参数,如Handlers.add('balance', Handlers.utils.hasMatchingTag("Action", "Balance"), function(msg) { ... }) 。相较于之前的参数,pattern 更为灵活。当然 Handlers.add 中也已经支持了这个 pattern 的方式。目前,可以有以下三种入参:
    • string:aos 会去自动匹配 Action 这个 Tag
    • table:aos 会把 table 中的 key 和 value 拆分开,分别去匹配 message 中所有的 Tag。其中 value 可以是一个 lua match expression,可以更方便的匹配 Tag 的内容。
    • function:这个方式就和之前的一模一样了

更符合语义的 REQ / RESP

案例

当前要介绍的新功能其实是对上述功能的扩展。大家都知道,AO 是基于 Actor Model 构建的。不同 Actor 之间的通讯,依靠的是消息的发送与接收。因此,Receive(pattern) 方法其实是与 AO 的设计浑然天成的。所以 aos 2.0 中就赋予了 message 两个内置的函数:receive 和 replay。

message.receive()

aos2.0@aos-2.0.0.rc2[Inbox:6]> .editor
<editor mode> use '.done' to submit or '.cancel' to cancel
local bal =  Send({Target = TestToken, Action = "Balance"}).receive().Data
print("bal: " .. bal)
.done

=========== print result ==============
bal: 0

如上面的代码,我们就可以根据 message 中内置的 receive() 方式实现了 Receive(pattern) 的功能,不需要自己再去写 pattern,是不是就好像 RPC 请求一样方便。在上面例子中我们拿到了查询的余额以后,就可以执行应该有的业务逻辑。代码看起来就相当的丝滑了。

同样的,spawn 一个进程后,也可以通过 local msg = Spawn(ao.env.Module.Id, {}).receive() 获取创建进程后的消息。

PS:对于旧的进程是无法进行这样的操作的。原因是 receive() 方法中是根据 Reference 这个 Tag 去查找返回的消息是否为需要回复接收的消息。而在之前旧的进程中,并没有 Reference 这个 Tag。但是 Receive(pattern) 依旧可以用的,需要自己构建 pattern 过滤出下一条消息为自己需要处理的消息。但是笔者建议,最好都升级下进程的 aos 版本。

3.png

message.replay({...})

Handlers.add('info', "Info", function(msg)
  msg.reply({
    Name = Name,
    Ticker = Ticker,
    Logo = Logo,
    Denomination = tostring(Denomination)
  })
end)

reply 方法则简单很多,更像是一个语法糖。上面的例子是 blue-print 中 token.lua 根据 aos 2.0 进行的改造。原来 msg.reply 是一个 ao.send 方法,但是在参数列表中少了 Target 这个参数。相当于开发者可以不用再关系 msg 的发送者,reply 函数会自动找到应该回复消息的进程。

message.forward({...})

forward 方法是一个消息转发的工具方法。以下引用一个官方的例子,更容易理解。

进程 C 向进程 B 发送了一条消息,进程 B 在 Handler 中转发给了 A,并修改了其中的 Data 的值。同样的,进程 A 也在 Handler 中转发给了 C,也修改了 Data 的值。

如果在没有 forward 的时候,我们则需要构建一个完整的消息体,把所有值都赋值到新的消息体中。现在可以完全的省略这些操作。所有需要转发的字段,都会按照标准的格式转发。

-- 进程 A
Handlers.add("Greeting", "Greeting", function (msg)
  msg.forward(msg['X-Origin'], { Data = msg.Data .. " Squarepants"})
end)

-- 进程 B
local process_a_id = "Process A id"
Handlers.add("Greeting", "Greeting", function (msg)
  msg.forward(process_a_id , { Data = "Hello " .. msg.Data })
end)

-- 进程 C
local process_b_id = "Process B id"
local res = Send({Target = process_b_id, Action = "Greeting", Data = "SpongeBob"}).receive("Process A id")
print(res.Data)

Handler 升级

aos 2.0 中的 Handler 含有 4 个参数,aos 2.0 之前是 3 个参数。

名称 类型 说明
name string Handlers 本质上是一个 lua 的 table,因此 name就是在 Handlers 中唯一标识该 Handler 的 key
pattern string table function 收到的消息是否匹配当前 Handler
handle function 消息处理函数,主要的业务逻辑
MaxRuns number (optional) 当前 Handler 最多运行几次。

这里需要扩展讲下 pattern、handle 和 MaxRuns,因为这三个参数是 aos 2.0 升级的内容。另外,还有一些其他功能的升级。

  1. 其中 pattern 其实跟 Receive(pattern) 中的 pattern 是一致的。在 aos 2.0 之前,代码如下:
Handlers.add("Get-Balance", function (msg) return msg.Action == "Balance", doBalance)

即 function (msg) return msg.Action == "Balance" 是判断收到的消息是否匹配当前 Handler。在 aos 2.0 之后的 pattern 就更加的灵活,由于前面提到过 Receive(pattern) 中的 pattern。因此,此处就不再赘述。

  1. MaxRuns:该参数为可选参数,如果不输入 MaxRuns,则表示当前 Handler 可以无限次执行。也就是和 aos 2.0 之前的 Handler 一致。如果输入了,则当当前 Handler 达到运行次数后,会从 Handlers 中删除该 Handler。
  2. 之前在 Receive(pattern) 这一部分讲到的 Handlers.once(),其实就是内置的一个函数,把 MaxRuns设置为 1 的 Handlers.add(...)。
  3. Handler 中新增了 Resolvers 用法。Resolvers 用法可以让你的代码看起来更加的整洁,减少大段的 if - else 结构。下面看个官方的例子,添加如下的 Handler
Handlers.add("foobarbaz", { Action = "Update" }, {
  [{ Status = "foo" }] = function (msg) print("foo") end,
  [{ Status = "bar" }] = function (msg) print("bar") end,
  [{ Status = "baz" }] = function (msg) print("baz") end
})

那么如果我如下发送消息的时候,则会有以下的返回。显而易见的是,不需要在 handle function 中对 Status 进行值的判断。Handler 中会自动匹配到当前消息中 Status 的值,所对应的函数。

aos2.0@aos-2.0.0.rc2[Inbox:6]> Send({Target = ao.id, Action = "Update", Tags = { Status = "foo"} })
{
   receive = function: 0x29bc5e0,
   onReply = function: 0x29bc480,
   output = "Message added to outbox"
}

=========== print result ==============
foo

其他更新

  1. 现在所有的消息都需要 Authority Tag,通过 aos 2.0 发送的消息都会默认带有 fcoN_xJeisVsPXA-trzVAuIiqO3ydLQxM-L4XbrQKzY 这个地址作为 Authority Tag

  2. os.time 终于可以使用了。他是根据接收到的 message 中的 Timestamp 去更新 os.time 的数值。

  3. aos 加了新的命令行,后续创建一个 sqlite process 不需要再去记住 sqlite module id 了,直接输入 aos --sqlite 就可以了。

  4. 在 .editor 中输入代码的时候,如果不小心输入错了一行,那么在 aos 2.0 之前只能通过 .cancel 重头再来。现在可以通过 .delete 删除上一行错误代码。不用再重新来过啦。

非 AOS 2.0 的一些重要更新

  • APM,全称是 AO package management。很多扩展包可以通过 apm 进行加载,就比如加载最近 AO 上大火的大模型。如下代码所示,
-- Install APM
aos> .load-blueprint apm

-- Loading...  apm
-- 📦 Loaded APM Client

-- Update APM
aos> APM.update()

-- 📤 Update request sent
-- ℹ Attempting to load client 1.1.0
-- 📦 Loaded APM Client
-- ✨ Client has been updated to 1.1.0

-- Install Llama-Herder
aos> APM.install("@sam/Llama-Herder")
  • DbAdmin 是 aos 的核心开发者提供的一个操作 sqlite 的工具类。直接通过 .load 加载该文件会出现内存溢出的错误。正确的引入方法如下,需要先下载 DbAdmin.lua 的代码,放在自己的项目工程中。然后在需要操作 sqlite 的文件中 require("DbAdmin")。

友情链接:https://github.com/twilson63/aos-packages/blob/main/packages/db-admin/src/DbAdmin.lua

local sqlite3 = require("lsqlite3")
local dbAdmin = require("DbAdmin")

-- Open the database
local db = sqlite3.open_memory()

-- Create a new dbAdmin instance
DbAdmin = dbAdmin.new(db)

-- to use DbAdmin
print(DbAdmin:tables())

结尾

强大的 aos 2.0 给开发者带来了开发效率的巨大提示。但是由于 ao 与 aos 发展的非常快,相关的文档不能说是很完善(尤其是中文文档)影响了生态接入的速度。大家有什么想法欢迎沟通交流,一起完善生态。

PS:aos 2.0 的不少通讯方法,在使用的时候,需要通讯方法的进程都是 aos 2.0 的版本才能生效。笔者建议,构建项目的时候需要统一版本,并且最好用 aos 2.0 直接创建的 process,而不是升级上来的 process。可以避免很多不必要的坑。


a.png

免责声明:本文不代表 PermaDAO 的观点或立场。PermaDAO 不提供投资建议,亦不为任何项目背书。请读者遵守所在国法律,合规进行 Web3 活动。

🔗 关于 PermaDAO:Website | Twitter | Telegram | Discord | MediumYoutube

点赞 0
收藏 0
分享

0 条评论

请先 登录 后评论
PermaDAO
PermaDAO
0x40F9...8718
Arweave 生态系统的共建者 DAO。 @ArweaveEco will be adopted by more developers. All projects of Arweave ecology can post their tasks and rewards here. @everVisionHQ@permaswap@ArweaveSCP