-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathasyncify.go
170 lines (131 loc) · 4.81 KB
/
asyncify.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
package asyncify
// Promise creates and returns a new Promise object that represents the eventual completion
// (or failure) of an asynchronous operation, and executes the specified executor function
// immediately. The executor function takes two arguments, a resolve function and a reject function,
// that allow the promise to be resolved with a value or rejected with a reason, respectively.
// The Promise object returned by this function has methods `Then`, `Catch`, and `Finally`
// that allow for chaining and handling of the eventual fulfillment or rejection of the promise.
// The Promise object also provides a blocking `Await` method that can be used to wait for the
// promise to be resolved or rejected.
func Promise(executor func(resolve func(interface{}), reject func(error))) *PromiseStruct {
promise := &PromiseStruct{
state: pending,
awaitChan: make(chan struct{}),
}
resolve := func(result interface{}) {
if promise.state != pending {
return
}
if promise.thenFn != nil {
promise.result = promise.thenFn(result)
} else {
promise.result = result
}
promise.state = fulfilled
if promise.finallyFn != nil {
promise.finallyFn()
}
close(promise.awaitChan)
}
reject := func(err error) {
if promise.state != pending {
return
}
if promise.catchFn != nil {
promise.result = promise.catchFn(err)
} else {
promise.err = err
}
promise.state = rejected
if promise.finallyFn != nil {
promise.finallyFn()
}
close(promise.awaitChan)
}
go executor(resolve, reject)
return promise
}
// Then registers a callback function to be called when the promise is resolved. If the promise is already resolved, the callback is called immediately with the resolved value. If the promise is rejected, the callback is skipped.
// The callback function takes one argument, the resolved value of the promise, and should return a value or a new promise that will be resolved with that value.
// Returns a new promise that is resolved with the return value of the callback function or rejected with the same reason as the original promise, if the callback function throws an error.
func (p *PromiseStruct) Then(fn func(interface{}) interface{}) *PromiseStruct {
promise := &PromiseStruct{
state: pending,
awaitChan: make(chan struct{}),
}
p.thenFn = fn
if p.state == fulfilled {
if p.thenFn != nil {
promise.result = p.thenFn(p.result)
} else {
promise.result = p.result
}
promise.state = fulfilled
if p.finallyFn != nil {
p.finallyFn()
}
close(promise.awaitChan)
} else if p.state == rejected {
if p.catchFn != nil {
promise.result = p.catchFn(p.err)
} else {
promise.err = p.err
}
promise.state = rejected
if p.finallyFn != nil {
p.finallyFn()
}
close(promise.awaitChan)
}
return promise
}
// Catch registers a callback function to be called when the promise is rejected. If the promise is already rejected, the callback is called immediately with the rejection reason. If the promise is resolved, the callback is skipped.
// The callback function takes one argument, the rejection reason of the promise, and should return a value or a new promise that will be resolved with that value.
// Returns a new promise that is resolved with the return value of the callback function or rejected with the same reason as the original promise, if the callback function throws an error.
func (p *PromiseStruct) Catch(fn func(error) interface{}) *PromiseStruct {
promise := &PromiseStruct{
state: pending,
awaitChan: make(chan struct{}),
}
p.catchFn = fn
if p.state == fulfilled {
promise.result = p.result
promise.state = fulfilled
if p.finallyFn != nil {
p.finallyFn()
}
close(promise.awaitChan)
} else if p.state == rejected {
if p.catchFn != nil {
promise.result = p.catchFn(p.err)
} else {
promise.err = p.err
}
promise.state = rejected
if p.finallyFn != nil {
p.finallyFn()
}
close(promise.awaitChan)
}
return promise
}
// Finally registers a callback function to be called when the promise is either resolved or rejected. If the promise is already resolved or rejected, the callback is called immediately.
// The callback function takes no arguments and should not return anything.
// Returns the same promise instance to allow for chaining of methods.
func (promise *PromiseStruct) Finally(fn func()) *PromiseStruct {
promise.finallyFn = fn
if promise.state != pending {
promise.finallyFn()
}
return promise
}
// Await blocks the execution of the program until the promise resolves or rejects,
// and returns either the resolved value or an error.
// It returns an error only if the promise was rejected, and the resolved value otherwise.
func (promise *PromiseStruct) Await() (interface{}, error) {
<-promise.awaitChan
if promise.state == rejected {
return nil, promise.err
}
return promise.result, nil
}