-
Notifications
You must be signed in to change notification settings - Fork 0
/
promise.go
171 lines (142 loc) · 4.66 KB
/
promise.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
//go:build js && wasm
// +build js,wasm
package gowasm
import (
"errors"
"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))
}
// Await waits for the Promise. It unmarshals the resolved value to v. An error
// will be returned if unmarshalling is unsuccessful or the Promise rejects.
// It is implemented by calling then and catch on JS.
func (p Promise) Await(v interface{}) error {
err := make(chan error)
p.value.Call("then", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
if len(args) > 0 && v != nil {
err <- FromJSValue(args[0], v)
return nil
}
err <- nil
return nil
}))
p.value.Call("catch", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
err <- errors.New(args[0].Call("toString").String())
return nil
}))
return <-err
}
// 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
}