diff --git a/concurrency.go b/concurrency.go index 2d6fd871..98bd0e25 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,35 @@ func Async6[A any, B any, C any, D any, E any, F any](f func() (A, B, C, D, E, F }() 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) + defer timer.Stop() + + ticker := time.NewTicker(tick) + defer ticker.Stop() + + i := 0 + + for tick := ticker.C; ; { + select { + case <-timer.C: + return false + case <-tick: + tick = nil + currentIndex := i + i++ + go func() { ch <- condition(currentIndex) }() + case v := <-ch: + if v { + return true + } + + tick = ticker.C + } + + } +} 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)) +}