diff --git a/concurrency.go b/concurrency.go index 31a62dde..259cc8e5 100644 --- a/concurrency.go +++ b/concurrency.go @@ -1,6 +1,9 @@ package lo -import "sync" +import ( + "sync" + "time" +) type synchronize struct { locker sync.Locker @@ -93,3 +96,33 @@ func Async6[A, B, C, D, E, F any](f func() (A, B, C, D, E, F)) <-chan Tuple6[A, }() return ch } + +// WaitFor runs periodically until a condition is validated. +func WaitFor(condition func(i int) bool, maxDuration time.Duration, tick time.Duration) bool { + ch := make(chan bool, 1) + timer := time.NewTimer(maxDuration) + ticker := time.NewTicker(tick) + + defer func() { + close(ch) + timer.Stop() + ticker.Stop() + }() + + i := 0 + + for { + select { + case <-timer.C: + return false + case <-ticker.C: + currentIndex := i + i++ + ch <- condition(currentIndex) + case v := <-ch: + if v { + return true + } + } + } +} diff --git a/concurrency_test.go b/concurrency_test.go index ae65efdd..953fcacb 100644 --- a/concurrency_test.go +++ b/concurrency_test.go @@ -212,3 +212,32 @@ func TestAsyncX(t *testing.T) { } } } + +func TestWaitFor(t *testing.T) { + t.Parallel() + testWithTimeout(t, 100*time.Millisecond) + is := assert.New(t) + + alwaysTrue := func(i int) bool { return true } + alwaysFalse := func(i int) bool { return false } + + is.True(WaitFor(alwaysTrue, 10*time.Millisecond, time.Millisecond)) + is.False(WaitFor(alwaysFalse, 10*time.Millisecond, time.Millisecond)) + + laterTrue := func(i int) bool { + return i > 5 + } + + is.True(WaitFor(laterTrue, 10*time.Millisecond, time.Millisecond)) + is.False(WaitFor(laterTrue, 10*time.Millisecond, 5*time.Millisecond)) + + counter := 0 + + alwaysFalse = func(i int) bool { + is.Equal(counter, i) + counter++ + return false + } + + is.False(WaitFor(alwaysFalse, 10*time.Millisecond, time.Millisecond)) +}