goja
goja copied to clipboard
enhanced Runtime.wrapReflectFunc(return Promise for js)
I have used goja deeply, and the biggest benefit it gives me is that Runtime.wrapReflectFunc can easily call go code (for example, calling the go standard library with js has brought me great convenience).
However, wrapReflectFunc cannot be customized and does not provide too many options, which caused me to encounter a lot of embarrassment when calling go code with js, mainly the following three problems:
- When the last return value error of go is not nil, goja will throw an exception for js. This prevents me from calling functions like errors.New etc. In addition, when the error of the io.Writer interface is not nil, the first return value n int may return the number of bytes written, and the exception thrown by goja prevents me from getting n.
- The int64/uint64 returned by some go functions exceeds the maximum integer of js and will lose precision, which makes me unable to use some go functions normally. Of course, you can make additional adaptations for these special functions, but this is obviously not as convenient as using wrapReflectFunc, especially Adapting individual member functions of struct is awkward.
- wrapReflectFunc will block the call, which causes the world of js to pause when calling time-consuming go functions, and since goja has built-in support for Promise, it should be a matter of course for wrapReflectFunc to support starting a new goroutine execution function and return Promise for js.
About two years ago, I submitted a piece of code. At that time, the plan was to allow hook wrapReflectFunc so that users could customize some wrapReflectFunc behaviors but it was not merged. Now this code is a new solution I thought of, which is simpler than before and will not cause much change to the existing code of goja, which will not affect efficiency.
When js calls the go function through wrapReflectFunc, an optional tag can be passed in. If no tag is passed in, goja will work according to the previous code. If it is passed in, it will work according to the tag and the parameter position of the go function will be moved backward in turn.
This will hardly cause any compatibility issues, nor will it have any impact on efficiency, but it can help people who need to solve the above three problems. I hope you can consider merging this code, even if you don't, please consider other official solutions to the above problems.
This code mainly adds three tags:
-
GoRaw Set this flag wrapReflectFunc will not throw an exception because of the error return value but will return error as the return value to js.
// will throw errr errors.New("test") // will get return error, and not throw any const e = errors.New(GoRaw , "test")
-
GoNumber Setting this flag wrapReflectFunc will convert the js return value
int64->goja.Int64
uint64->goja.Uint64
[]int64->[]goja.Int64
[]uint64->[]goja.Uint64
, This guarantees no loss of precision for int64 uint64 (also defines common math functions for Int64 Uint64 ).// Returning number may lose precision strconv.ParseInt(s, 10, 64) // Returning object, the implementation is a goja.Int64 without loss of precision strconv.ParseInt(GoNumber, s, 10, 64)
-
GoAsync Set this flag to execute the function in a new goroutine, js will immediately return a Promise. (You need to call the Runtime.SetRunOnLoop function to set the RunOnLoop function provided by the event loop to the Runtime, because Promise must rely on the event loop)
// Blocking calls, all code in js will stop time.Sleep(time.Second) // This will be called asynchronously and return a Promise immediately without blocking, and will be notified by the Promise when the function call completes time.Sleep(time.Second).then(()=>{ // go function call completed })
In addition, several tags are defined for js, which are combinations of the above three tags, the full markup is as follows:
- GoRaw
- GoNumber
- GoRawNumber
- GoAsync
- GoAsyncRaw
- GoAsyncNumber
- GoAsyncRawNumber