package jscriptx_test import ( "context" "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 + "]" } // 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: 两个形参的回调必须返回结果", jscriptx.ErrUnsupportedSignature) } 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", jscriptx.ErrUnsupportedSignature, 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 → [输入] }