feat: Implement Promise
parent
392b175abf
commit
f3864a59ca
@ -0,0 +1,13 @@
|
||||
package wasm
|
||||
|
||||
import "syscall/js"
|
||||
|
||||
// NewError returns a JS Error with the provided Go error's error message.
|
||||
func NewError(goErr error) js.Value {
|
||||
errConstructor, err := Global().Expect(js.TypeFunction, "Error")
|
||||
if err != nil {
|
||||
panic("Error constructor not found")
|
||||
}
|
||||
|
||||
return errConstructor.New(goErr.Error())
|
||||
}
|
@ -0,0 +1,145 @@
|
||||
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
|
||||
}
|
Loading…
Reference in New Issue