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  }