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 都仍能编译。
97 lines
2.7 KiB
Go
97 lines
2.7 KiB
Go
package jscriptx_test
|
||
|
||
import (
|
||
"context"
|
||
"errors"
|
||
"fmt"
|
||
"log"
|
||
|
||
"git.fsdpf.net/go/jscriptx"
|
||
)
|
||
|
||
// Caller 用来实现自定义的调用约定——本库不预设脚本回调该长什么样,
|
||
// 这个例子实现的是「回调 + next」这种中间件式的约定:脚本函数写成哪种形状,
|
||
// 运行时探测出来自动适配,脚本作者不用额外声明签名。
|
||
//
|
||
// 它同时也说明了为什么需要 Caller 而不是直接用 Call:next 是个 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 → [输入]
|
||
}
|