Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enhanced Runtime.wrapReflectFunc(return Promise for js) #490

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

zuiwuchang
Copy link

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:

  1. 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.
  2. 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.
  3. 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:

  1. 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")
    
  2. 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)
    
  3. 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

@GreyXor
Copy link

GreyXor commented Sep 2, 2023

@zuiwuchang hi, any news ? pretty interested about it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants