github.com/guyezi/gofrontend@v0.0.0-20200228202240-7a62a49e62c0/libgo/go/syscall/js/func.go (about) 1 // Copyright 2018 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // +build js,wasm 6 7 package js 8 9 import "sync" 10 11 var ( 12 funcsMu sync.Mutex 13 funcs = make(map[uint32]func(Value, []Value) interface{}) 14 nextFuncID uint32 = 1 15 ) 16 17 var _ Wrapper = Func{} // Func must implement Wrapper 18 19 // Func is a wrapped Go function to be called by JavaScript. 20 type Func struct { 21 Value // the JavaScript function that invokes the Go function 22 id uint32 23 } 24 25 // FuncOf returns a wrapped function. 26 // 27 // Invoking the JavaScript function will synchronously call the Go function fn with the value of JavaScript's 28 // "this" keyword and the arguments of the invocation. 29 // The return value of the invocation is the result of the Go function mapped back to JavaScript according to ValueOf. 30 // 31 // A wrapped function triggered during a call from Go to JavaScript gets executed on the same goroutine. 32 // A wrapped function triggered by JavaScript's event loop gets executed on an extra goroutine. 33 // Blocking operations in the wrapped function will block the event loop. 34 // As a consequence, if one wrapped function blocks, other wrapped funcs will not be processed. 35 // A blocking function should therefore explicitly start a new goroutine. 36 // 37 // Func.Release must be called to free up resources when the function will not be used any more. 38 func FuncOf(fn func(this Value, args []Value) interface{}) Func { 39 funcsMu.Lock() 40 id := nextFuncID 41 nextFuncID++ 42 funcs[id] = fn 43 funcsMu.Unlock() 44 return Func{ 45 id: id, 46 Value: jsGo.Call("_makeFuncWrapper", id), 47 } 48 } 49 50 // Release frees up resources allocated for the function. 51 // The function must not be invoked after calling Release. 52 func (c Func) Release() { 53 funcsMu.Lock() 54 delete(funcs, c.id) 55 funcsMu.Unlock() 56 } 57 58 // setEventHandler is defined in the runtime package. 59 func setEventHandler(fn func()) 60 61 func init() { 62 setEventHandler(handleEvent) 63 } 64 65 func handleEvent() { 66 cb := jsGo.Get("_pendingEvent") 67 if cb.IsNull() { 68 return 69 } 70 jsGo.Set("_pendingEvent", Null()) 71 72 id := uint32(cb.Get("id").Int()) 73 if id == 0 { // zero indicates deadlock 74 select {} 75 } 76 funcsMu.Lock() 77 f, ok := funcs[id] 78 funcsMu.Unlock() 79 if !ok { 80 Global().Get("console").Call("error", "call to released function") 81 return 82 } 83 84 this := cb.Get("this") 85 argsObj := cb.Get("args") 86 args := make([]Value, argsObj.Length()) 87 for i := range args { 88 args[i] = argsObj.Index(i) 89 } 90 result := f(this, args) 91 cb.Set("result", result) 92 }