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 }