github.com/0chain/gosdk@v1.17.11/wasmsdk/jsbridge/async.go (about)

     1  //go:build js && wasm
     2  // +build js,wasm
     3  
     4  package jsbridge
     5  
     6  import (
     7  	"fmt"
     8  	"reflect"
     9  	"syscall/js"
    10  )
    11  
    12  type AsyncInvoker func(resolve, reject js.Value, fn reflect.Value, in []reflect.Value, err error)
    13  
    14  func Async(funcType reflect.Type) (AsyncInvoker, error) {
    15  
    16  	outputBinder, err := NewOutputBuilder(funcType).Build()
    17  	if err != nil {
    18  		return nil, err
    19  	}
    20  
    21  	switch funcType.NumOut() {
    22  	case 0:
    23  		return func(resolve, reject js.Value, fn reflect.Value, in []reflect.Value, err error) {
    24  			defer func() {
    25  				if r := recover(); r != nil {
    26  					fmt.Println("[recover]", r)
    27  				}
    28  			}()
    29  			if err != nil {
    30  				jsErr := NewJsError(err.Error())
    31  				resolve.Invoke(js.ValueOf(jsErr))
    32  				return
    33  			}
    34  
    35  			fn.Call(in)
    36  			resolve.Invoke()
    37  
    38  		}, nil
    39  	case 1:
    40  
    41  		outputType := funcType.Out(0)
    42  		//func(...)error
    43  		if outputType.String() == TypeError {
    44  			return func(resolve, reject js.Value, fn reflect.Value, in []reflect.Value, err error) {
    45  				defer func() {
    46  					if r := recover(); r != nil {
    47  						fmt.Println("[recover]", r)
    48  					}
    49  				}()
    50  				if err != nil {
    51  					jsErr := NewJsError(err.Error())
    52  					resolve.Invoke(js.ValueOf(jsErr))
    53  					return
    54  				}
    55  
    56  				output := fn.Call(in)
    57  
    58  				if output[0].IsNil() {
    59  					resolve.Invoke()
    60  				} else {
    61  					args := outputBinder(output)
    62  					resolve.Invoke(args[0])
    63  				}
    64  			}, nil
    65  		} else { //func(...) T
    66  			return func(resolve, reject js.Value, fn reflect.Value, in []reflect.Value, err error) {
    67  				if err != nil {
    68  					jsErr := NewJsError(err.Error())
    69  					resolve.Invoke(js.ValueOf(jsErr))
    70  					return
    71  				}
    72  
    73  				output := fn.Call(in)
    74  				args := outputBinder(output)
    75  				resolve.Invoke(args[0])
    76  			}, nil
    77  		}
    78  	case 2:
    79  
    80  		errOutputType := funcType.Out(1)
    81  
    82  		if errOutputType.String() != TypeError {
    83  			return nil, ErrFuncNotSupported
    84  		}
    85  		//func(...) (T,error)
    86  		return func(resolve, reject js.Value, fn reflect.Value, in []reflect.Value, err error) {
    87  			defer func() {
    88  				if r := recover(); r != nil {
    89  					fmt.Println("[recover]", r)
    90  				}
    91  			}()
    92  			if err != nil {
    93  				jsErr := NewJsError(err.Error())
    94  				resolve.Invoke(js.ValueOf(jsErr))
    95  				return
    96  			}
    97  
    98  			output := fn.Call(in)
    99  
   100  			args := outputBinder(output)
   101  			if output[1].IsNil() {
   102  				resolve.Invoke(args[0])
   103  			} else {
   104  				resolve.Invoke(args[1])
   105  			}
   106  
   107  		}, nil
   108  
   109  	default:
   110  		return nil, ErrFuncNotSupported
   111  	}
   112  
   113  }
   114  
   115  // This function try to execute wasm functions that are wrapped with "Promise"
   116  // see: https://stackoverflow.com/questions/68426700/how-to-wait-a-js-async-function-from-golang-wasm/68427221#comment120939975_68427221
   117  func Await(awaitable js.Value) ([]js.Value, []js.Value) {
   118  	then := make(chan []js.Value)
   119  	defer close(then)
   120  	thenFunc := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
   121  		then <- args
   122  		return nil
   123  	})
   124  	defer thenFunc.Release()
   125  
   126  	catch := make(chan []js.Value)
   127  	defer close(catch)
   128  	catchFunc := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
   129  		errObj := args[0]
   130  
   131  		// Print the error value
   132  		errValue := errObj.Get("message").String()
   133  		fmt.Println("catch error value:", errValue)
   134  
   135  		catch <- args
   136  		return nil
   137  	})
   138  	defer catchFunc.Release()
   139  
   140  	awaitable.Call("then", thenFunc).Call("catch", catchFunc)
   141  
   142  	select {
   143  	case result := <-then:
   144  		return result, []js.Value{js.Null()}
   145  	case err := <-catch:
   146  		return []js.Value{js.Null()}, err
   147  	}
   148  }