forked from Team-Ortix/golang-wasm
182 lines
4.9 KiB
Go
182 lines
4.9 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
|
|
}
|
|
|
|
// Await waits for the promise to be fulfilled or rejected.
|
|
// It returns an error if there was an error unmarshalling or interacting with JS.
|
|
//
|
|
// This function returns true and sets the value of 'out' if the promise is fulfilled.
|
|
// It returns false and sets the value of 'rej' if the promise is rejected.
|
|
func (p Promise) Await(out, rej interface{}) (bool, error) {
|
|
resultChan := make(chan js.Value)
|
|
errChan := make(chan js.Value)
|
|
|
|
next, err := p.Get("next")
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
catch, err := p.Get("catch")
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
next.Invoke(ToJSValue(func(_, result js.Value) js.Value {
|
|
resultChan <- result
|
|
return result
|
|
}))
|
|
catch.Invoke(ToJSValue(func(_, err js.Value) js.Value {
|
|
errChan <- err
|
|
return err
|
|
}))
|
|
|
|
select {
|
|
case jsOut := <-resultChan:
|
|
return true, FromJSValue(jsOut, out)
|
|
case jsErr := <-errChan:
|
|
return false, FromJSValue(jsErr, rej)
|
|
}
|
|
}
|
|
|
|
// 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
|
|
}
|