Black Lives Matter. Support the Equal Justice Initiative.

Source file src/syscall/js/func.go

Documentation: syscall/js

     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 function to be used by JavaScript.
    26  //
    27  // The Go function fn is called with the value of JavaScript's "this" keyword and the
    28  // arguments of the invocation. The return value of the invocation is
    29  // the result of the Go function mapped back to JavaScript according to ValueOf.
    30  //
    31  // Invoking the wrapped Go function from JavaScript will
    32  // pause the event loop and spawn a new goroutine.
    33  // Other wrapped functions which are triggered during a call from Go to JavaScript
    34  // get executed on the same goroutine.
    35  //
    36  // As a consequence, if one wrapped function blocks, JavaScript's event loop
    37  // is blocked until that function returns. Hence, calling any async JavaScript
    38  // API, which requires the event loop, like fetch (http.Client), will cause an
    39  // immediate deadlock. Therefore a blocking function should explicitly start a
    40  // new goroutine.
    41  //
    42  // Func.Release must be called to free up resources when the function will not be invoked any more.
    43  func FuncOf(fn func(this Value, args []Value) interface{}) Func {
    44  	funcsMu.Lock()
    45  	id := nextFuncID
    46  	nextFuncID++
    47  	funcs[id] = fn
    48  	funcsMu.Unlock()
    49  	return Func{
    50  		id:    id,
    51  		Value: jsGo.Call("_makeFuncWrapper", id),
    52  	}
    53  }
    54  
    55  // Release frees up resources allocated for the function.
    56  // The function must not be invoked after calling Release.
    57  // It is allowed to call Release while the function is still running.
    58  func (c Func) Release() {
    59  	funcsMu.Lock()
    60  	delete(funcs, c.id)
    61  	funcsMu.Unlock()
    62  }
    63  
    64  // setEventHandler is defined in the runtime package.
    65  func setEventHandler(fn func())
    66  
    67  func init() {
    68  	setEventHandler(handleEvent)
    69  }
    70  
    71  func handleEvent() {
    72  	cb := jsGo.Get("_pendingEvent")
    73  	if cb.IsNull() {
    74  		return
    75  	}
    76  	jsGo.Set("_pendingEvent", Null())
    77  
    78  	id := uint32(cb.Get("id").Int())
    79  	if id == 0 { // zero indicates deadlock
    80  		select {}
    81  	}
    82  	funcsMu.Lock()
    83  	f, ok := funcs[id]
    84  	funcsMu.Unlock()
    85  	if !ok {
    86  		Global().Get("console").Call("error", "call to released function")
    87  		return
    88  	}
    89  
    90  	this := cb.Get("this")
    91  	argsObj := cb.Get("args")
    92  	args := make([]Value, argsObj.Length())
    93  	for i := range args {
    94  		args[i] = argsObj.Index(i)
    95  	}
    96  	result := f(this, args)
    97  	cb.Set("result", result)
    98  }
    99  

View as plain text