golang-wasm/wasm/promise.go

146 lines
4.0 KiB
Go

package wasm
import "syscall/js"
// Promise is an instance of a JS promise.
// The zero value of this struct is not a valid Promise.
type Promise struct {
Object
}
// FromJSValue turns a JS value to a Promise.
func (p *Promise) FromJSValue(value js.Value) error {
var err error
p.Object, err = NewObject(value)
return err
}
// NewPromise returns a promise that is fulfilled or rejected when the provided handler returns.
// The handler is spawned in its own goroutine.
func NewPromise(handler func() (interface{}, error)) Promise {
resultChan := make(chan interface{})
errChan := make(chan error)
// Invoke the handler in a new goroutine.
go func() {
result, err := handler()
if err != nil {
errChan <- err
return
}
resultChan <- result
}()
// Create a JS promise handler.
var jsHandler js.Func
jsHandler = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
if len(args) < 2 {
panic("not enough arguments are passed to the Promise constructor handler")
}
resolve := args[0]
reject := args[1]
if resolve.Type() != js.TypeFunction || reject.Type() != js.TypeFunction {
panic("invalid type passed to Promise constructor handler")
}
go func() {
select {
case r := <-resultChan:
resolve.Invoke(ToJSValue(r))
case err := <-errChan:
reject.Invoke(NewError(err))
}
// Free up resources now that we are done.
jsHandler.Release()
}()
return nil
})
promise, err := Global().Expect(js.TypeFunction, "Promise")
if err != nil {
panic("Promise constructor not found")
}
return mustJSValueToPromise(promise.New(jsHandler))
}
// PromiseAll creates a promise that is fulfilled when all the provided promises have been fulfilled.
// The promise is rejected when any of the promises provided rejects.
// It is implemented by calling Promise.all on JS.
func PromiseAll(promise ...Promise) Promise {
promiseAll, err := Global().Expect(js.TypeFunction, "Promise", "all")
if err != nil {
panic("Promise.all not found")
}
pInterface := make([]interface{}, 0, len(promise))
for _, v := range promise {
pInterface = append(pInterface, v)
}
return mustJSValueToPromise(promiseAll.Invoke(pInterface))
}
// PromiseAllSettled creates a promise that is fulfilled when all the provided promises have been fulfilled or rejected.
// It is implemented by calling Promise.allSettled on JS.
func PromiseAllSettled(promise ...Promise) Promise {
promiseAllSettled, err := Global().Expect(js.TypeFunction, "Promise", "allSettled")
if err != nil {
panic("Promise.allSettled not found")
}
pInterface := make([]interface{}, 0, len(promise))
for _, v := range promise {
pInterface = append(pInterface, v)
}
return mustJSValueToPromise(promiseAllSettled.Invoke(pInterface))
}
// PromiseAny creates a promise that is fulfilled when any of the provided promises have been fulfilled.
// The promise is rejected when all of the provided promises gets rejected.
// It is implemented by calling Promise.any on JS.
func PromiseAny(promise ...Promise) Promise {
promiseAny, err := Global().Expect(js.TypeFunction, "Promise", "any")
if err != nil {
panic("Promise.any not found")
}
pInterface := make([]interface{}, 0, len(promise))
for _, v := range promise {
pInterface = append(pInterface, v)
}
return mustJSValueToPromise(promiseAny.Invoke(pInterface))
}
// PromiseRace creates a promise that is fulfilled or rejected when one of the provided promises fulfill or reject.
// It is implemented by calling Promise.race on JS.
func PromiseRace(promise ...Promise) Promise {
promiseRace, err := Global().Expect(js.TypeFunction, "Promise", "race")
if err != nil {
panic("Promise.race not found")
}
pInterface := make([]interface{}, 0, len(promise))
for _, v := range promise {
pInterface = append(pInterface, v)
}
return mustJSValueToPromise(promiseRace.Invoke(pInterface))
}
func mustJSValueToPromise(v js.Value) Promise {
var p Promise
err := p.FromJSValue(v)
if err != nil {
panic("Expected a Promise from JS standard library")
}
return p
}