~kyoto-framework/zen

zen/async.go -rw-r--r-- 1.8 KiB
81acbab4Yurii Zinets minor update 12 days ago
                                                                                
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
/*
	-

	Async

	Zen provides a way to define and run asynchronous functions.
	It's based on Go's standard goroutines and channels.
	Future object holds value channel and error.
	It's used as an awaitable object.
	As far as Go is not provides an async/await syntax,
	your function must to return a Future, provided by Async function.

	Example:

		func Foo() *zen.Future[string] {
			return zen.Async(func() (string, error) {
				return "Bar", nil
			})
		}

		func main() {
			// Non-blocking calls
			fbar1 := Foo()
			fbar2 := Foo()
			// Await for results (errors are passed to simplify example)
			bar1, _ := zen.Await(fbar1)
			bar2, _ := zen.Await(fbar2)
		}

*/
package zen

import "encoding/json"

// Future is an awaitable object.
// Behavior is similar to JS Promise.
type Future[T any] struct {
	value chan T
	cache *T
	err   error
}

// MarshalJSON implements future marshalling.
func (f *Future[T]) MarshalJSON() ([]byte, error) {
	val, err := Await(f)
	if err != nil {
		return []byte{}, err
	}
	return json.Marshal(val)
}

// MarshalJSON implements future unmarshalling.
func (c *Future[T]) UnmarshalJSON(data []byte) error {
	return json.Unmarshal(data, &c.cache)
}

// Await for a future object.
func Await[T any](f *Future[T]) (T, error) {
	// Return from cache, if exists
	if f.cache != nil {
		return *f.cache, f.err
	}
	// Wait for value
	v := <-f.value
	// Save to cache
	f.cache = &v
	// Return
	return v, f.err
}

// Async runs a function in a goroutine and returns Future object for it.
func Async[T any](f func() (T, error)) *Future[T] {
	// Create future
	future := Future[T]{value: make(chan T)}
	// Run thread
	go func() {
		// Run function
		value, err := f()
		// Set error
		future.err = err
		// Set value
		future.value <- value
		// Close value channel
		close(future.value)
	}()
	// Return future
	return &future
}