Files
jscriptx/caller_example_test.go
T
what 9b3509ae29 refactor: 清掉指向不存在的 Dispatch 的文档,删掉为它留的死导出
Dispatch 在仓库里出现 7 次,全是注释和错误文案,没有任何实现。三处错误文案
写着「需要回调语义请用 Dispatch」——使用者按这句去查会找不到东西,真正该指的
是 WithCall。caller.go 那句还指向不存在的子包 jscriptx/dispatch。

连带删掉四个为它留的导出(全仓库零调用):

  Target                   统一 Script/Instance 的接口。有未导出方法 owner(),
                           外部实现不了;也没有任何函数以它为参数或返回值
  ErrUnsupportedSignature  哨兵错误,库自己从不产生它
  KindSignature            错误分类,全仓库唯一一次出现就是它自己的声明。
                           留着会让写 switch 的人为一个永不出现的分支写代码
  OverlayLoader.Loaders    零调用的 getter,连测试都没有

另外删掉 Instance.IdleFor 和 lastUsed 字段:它是给「空闲回收」用的,而
doc.go 明确写着本库不代管实例生命周期、没有空闲回收——字段注释和包文档直接
对立。代价是每次 Call 白付两次 time.Now() + atomic store。业务侧真要自己回收,
记一个时间戳是一行的事。

caller 的示例原来拿 ErrUnsupportedSignature 当哨兵,改成自己声明一个——
回调签名的约定本来就是调用方定的,哨兵该归调用方。

验证:framework-v2 和 lx-bid 都仍能编译。
2026-09-10 15:15:10 +08:00

97 lines
2.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package jscriptx_test
import (
"context"
"errors"
"fmt"
"log"
"git.fsdpf.net/go/jscriptx"
)
// Caller 用来实现自定义的调用约定——本库不预设脚本回调该长什么样,
// 这个例子实现的是「回调 + next」这种中间件式的约定:脚本函数写成哪种形状,
// 运行时探测出来自动适配,脚本作者不用额外声明签名。
//
// 它同时也说明了为什么需要 Caller 而不是直接用 Callnext 是个 Go 闭包,
// 得先看脚本声明了几个形参,才知道要不要把它传进去——这些都必须在同一次
// VM 借出期间完成。
func ExampleCaller() {
e, err := jscriptx.New(jscriptx.WithLogger(nil))
if err != nil {
log.Fatal(err)
}
defer e.Close()
// 三种回调写法,脚本作者挑顺手的用
s, err := e.Compile("ctrl.ts", `
export function twoArgs(v, next) { return next(v) + "|脚本自己调的 next" }
export function decorator(v) { return v + "|装饰过" }
export function sideEffect(v) { /* 只做事,不返回 */ }
`)
if err != nil {
log.Fatal(err)
}
next := func(v string) string { return "[" + v + "]" }
// 回调签名的约定是**调用方**定的(这里定成"1 个或 2 个形参"),
// 所以哨兵错误也归调用方声明,本库不提供。
errBadSignature := errors.New("回调签名不受支持")
// dispatch 按脚本函数的形状分派
dispatch := func(fn, value string) (string, error) {
var out string
err := s.WithCall(context.Background(), fn, func(c jscriptx.Caller) error {
switch n := c.Arity(); n {
case 2:
// handle(v, next):调不调 next 由脚本决定,但得把结果返回出来
res, err := c.Call(value, next)
if err != nil {
return err
}
if res.IsEmpty() {
return fmt.Errorf("%w: 两个形参的回调必须返回结果",
errBadSignature)
}
return res.Into(&out)
case 1:
// handle(v):有返回值当装饰器,没返回值当纯副作用
res, err := c.Call(value)
if err != nil {
return err
}
if res.IsEmpty() {
out = next(value)
return nil
}
var decorated string
if err := res.Into(&decorated); err != nil {
return err
}
out = next(decorated)
return nil
default:
return fmt.Errorf("%w: 形参个数必须是 1 或 2,当前是 %d",
errBadSignature, n)
}
})
return out, err
}
for _, fn := range []string{"twoArgs", "decorator", "sideEffect"} {
got, err := dispatch(fn, "输入")
if err != nil {
log.Fatal(err)
}
fmt.Printf("%-11s → %s\n", fn, got)
}
// Output:
// twoArgs → [输入]|脚本自己调的 next
// decorator → [输入|装饰过]
// sideEffect → [输入]
}