diff --git a/README.md b/README.md index 68c058dd..0890f8f3 100644 --- a/README.md +++ b/README.md @@ -1278,12 +1278,29 @@ Executes a function in a goroutine and returns the result in a channel. ```go ch := lo.Async(func() error { time.Sleep(10 * time.Second); return nil }) // chan error (nil) +``` + +### Async{0->6} + +Executes a function in a goroutine and returns the result in a channel. +For function with multiple return values, the results will be returned as a tuple inside the channel. +For function without return, struct{} will be returned in the channel. + +```go +ch := lo.Async0(func() { time.Sleep(10 * time.Second) }) +// chan struct{} + +ch := lo.Async1(func() int { + time.Sleep(10 * time.Second); + return 42 +}) +// chan int (42) -ch := lo.Async(func() lo.Tuple2[int, error] { +ch := lo.Async2(func() (int, string) { time.Sleep(10 * time.Second); - return lo.Tuple2[int, error]{42, nil} + return 42, "Hello" }) -// chan lo.Tuple2[int, error] ({42, nil}) +// chan lo.Tuple2[int, string] ({42, "Hello"}) ``` ### Must diff --git a/concurrency.go b/concurrency.go index d0d38454..2c112141 100644 --- a/concurrency.go +++ b/concurrency.go @@ -1,10 +1,70 @@ package lo // Async executes a function in a goroutine and returns the result in a channel. -func Async[T any](f func() T) chan T { - ch := make(chan T) +func Async[A any](f func() A) chan A { + ch := make(chan A) go func() { ch <- f() }() return ch } + +// Async0 executes a function in a goroutine and returns a channel set once the function finishes. +func Async0(f func()) chan struct{} { + ch := make(chan struct{}) + go func() { + f() + ch <- struct{}{} + }() + return ch +} + +// Async1 is an alias to Async. +func Async1[A any](f func() A) chan A { + return Async(f) +} + +// Async2 has the same behavior as Async, but returns the 2 results as a tuple inside the channel. +func Async2[A any, B any](f func() (A, B)) chan Tuple2[A, B] { + ch := make(chan Tuple2[A, B]) + go func() { + ch <- T2(f()) + }() + return ch +} + +// Async3 has the same behavior as Async, but returns the 3 results as a tuple inside the channel. +func Async3[A any, B any, C any](f func() (A, B, C)) chan Tuple3[A, B, C] { + ch := make(chan Tuple3[A, B, C]) + go func() { + ch <- T3(f()) + }() + return ch +} + +// Async4 has the same behavior as Async, but returns the 4 results as a tuple inside the channel. +func Async4[A any, B any, C any, D any](f func() (A, B, C, D)) chan Tuple4[A, B, C, D] { + ch := make(chan Tuple4[A, B, C, D]) + go func() { + ch <- T4(f()) + }() + return ch +} + +// Async5 has the same behavior as Async, but returns the 5 results as a tuple inside the channel. +func Async5[A any, B any, C any, D any, E any](f func() (A, B, C, D, E)) chan Tuple5[A, B, C, D, E] { + ch := make(chan Tuple5[A, B, C, D, E]) + go func() { + ch <- T5(f()) + }() + return ch +} + +// Async6 has the same behavior as Async, but returns the 6 results as a tuple inside the channel. +func Async6[A any, B any, C any, D any, E any, F any](f func() (A, B, C, D, E, F)) chan Tuple6[A, B, C, D, E, F] { + ch := make(chan Tuple6[A, B, C, D, E, F]) + go func() { + ch <- T6(f()) + }() + return ch +} diff --git a/concurrency_test.go b/concurrency_test.go index 03317e3d..d07407e4 100644 --- a/concurrency_test.go +++ b/concurrency_test.go @@ -26,3 +26,131 @@ func TestAsync(t *testing.T) { is.Fail("Async should not block") } } + +func TestAsyncX(t *testing.T) { + is := assert.New(t) + + { + sync := make(chan struct{}) + + ch := Async0(func() { + <-sync + }) + + sync <- struct{}{} + + select { + case <-ch: + case <-time.After(time.Millisecond): + is.Fail("Async0 should not block") + } + } + + { + sync := make(chan struct{}) + + ch := Async1(func() int { + <-sync + return 10 + }) + + sync <- struct{}{} + + select { + case result := <-ch: + is.Equal(result, 10) + case <-time.After(time.Millisecond): + is.Fail("Async1 should not block") + } + } + + { + sync := make(chan struct{}) + + ch := Async2(func() (int, string) { + <-sync + return 10, "Hello" + }) + + sync <- struct{}{} + + select { + case result := <-ch: + is.Equal(result, Tuple2[int, string]{10, "Hello"}) + case <-time.After(time.Millisecond): + is.Fail("Async2 should not block") + } + } + + { + sync := make(chan struct{}) + + ch := Async3(func() (int, string, bool) { + <-sync + return 10, "Hello", true + }) + + sync <- struct{}{} + + select { + case result := <-ch: + is.Equal(result, Tuple3[int, string, bool]{10, "Hello", true}) + case <-time.After(time.Millisecond): + is.Fail("Async3 should not block") + } + } + + { + sync := make(chan struct{}) + + ch := Async4(func() (int, string, bool, float64) { + <-sync + return 10, "Hello", true, 3.14 + }) + + sync <- struct{}{} + + select { + case result := <-ch: + is.Equal(result, Tuple4[int, string, bool, float64]{10, "Hello", true, 3.14}) + case <-time.After(time.Millisecond): + is.Fail("Async4 should not block") + } + } + + { + sync := make(chan struct{}) + + ch := Async5(func() (int, string, bool, float64, string) { + <-sync + return 10, "Hello", true, 3.14, "World" + }) + + sync <- struct{}{} + + select { + case result := <-ch: + is.Equal(result, Tuple5[int, string, bool, float64, string]{10, "Hello", true, 3.14, "World"}) + case <-time.After(time.Millisecond): + is.Fail("Async5 should not block") + } + } + + { + sync := make(chan struct{}) + + ch := Async6(func() (int, string, bool, float64, string, int) { + <-sync + return 10, "Hello", true, 3.14, "World", 100 + }) + + sync <- struct{}{} + + select { + case result := <-ch: + is.Equal(result, Tuple6[int, string, bool, float64, string, int]{10, "Hello", true, 3.14, "World", 100}) + case <-time.After(time.Millisecond): + is.Fail("Async6 should not block") + } + } +}