Skip to content

Commit 89ab017

Browse files
committed
Add AsyncCallback.countDownLatch
1 parent 0fe692c commit 89ab017

File tree

3 files changed

+85
-1
lines changed

3 files changed

+85
-1
lines changed

core/src/main/scala/japgolly/scalajs/react/AsyncCallback.scala

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,37 @@ object AsyncCallback {
5353
(promise, complete) <- promise[Unit]
5454
} yield new Barrier(promise, complete(tryUnit))
5555

56+
final class CountDownLatch(count: Int, barrier: Barrier) {
57+
private var _pending = count.max(0)
58+
59+
/** Decrements the count of the latch, releasing all waiting computations if the count reaches zero. */
60+
val countDown: Callback =
61+
Callback {
62+
if (_pending > 0) {
63+
_pending -= 1
64+
if (_pending == 0) {
65+
barrier.complete.runNow()
66+
}
67+
}
68+
}
69+
70+
def await: AsyncCallback[Unit] =
71+
barrier.await
72+
73+
def isComplete: CallbackTo[Boolean] =
74+
barrier.isComplete
75+
76+
def pending: CallbackTo[Int] =
77+
CallbackTo(_pending)
78+
}
79+
80+
/** A synchronization aid that allows you to wait until a set of async processes completes. */
81+
def countDownLatch(count: Int): CallbackTo[CountDownLatch] =
82+
for {
83+
b <- barrier
84+
_ <- b.complete.when_(count <= 0)
85+
} yield new CountDownLatch(count, b)
86+
5687
def first[A](f: (Try[A] => Callback) => Callback): AsyncCallback[A] =
5788
new AsyncCallback(g => CallbackTo {
5889
var first = true

doc/changelog/1.7.7.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
This entire release is focused on `AsyncCallback`.
44

5-
* `AsyncCallback` instances
5+
* `AsyncCallback` instances:
66

77
* Added `fork` and `fork_` which run the async computation in the background.
88

@@ -11,6 +11,20 @@ This entire release is focused on `AsyncCallback`.
1111

1212
`fork_` on the other hand, returns nothing and is effectively fire-and-forget.
1313

14+
* `AsyncCallback` object:
15+
16+
* Added `countDownLatch(count: Int)` which returns an `AsyncCallback.CountDownLatch`.
17+
It has the same purpose and semantics as Java's `CountDownLatch`.
18+
19+
```scala
20+
AsyncCallback.CountDownLatch {
21+
countDown : Callback
22+
await : AsyncCallback[Unit]
23+
isComplete: CallbackTo[Boolean]
24+
pending : CallbackTo[Int]
25+
}
26+
```
27+
1428
* `AsyncCallback.Barrier`:
1529

1630
* Added `isComplete: CallbackTo[Boolean]` to synchronously query whether the barrier is complete or not.

test/src/test/scala/japgolly/scalajs/react/core/AsyncCallbackTest.scala

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,5 +164,44 @@ object AsyncCallbackTest extends TestSuite {
164164
} yield ()
165165
}
166166

167+
"countDownLatch" - {
168+
"3" - asyncTest {
169+
var t = 0
170+
var completedAt = -1
171+
for {
172+
b <- AsyncCallback.barrier.asAsyncCallback
173+
l <- AsyncCallback.countDownLatch(3).asAsyncCallback
174+
_ <- l.await.flatMapSync(_ => {completedAt = t; b.complete}).fork_.asAsyncCallback
175+
_ <- l.pending.asAsyncCallback.tap(assertEq(_, 3))
176+
_ <- l.isComplete.asAsyncCallback.tap(assertEq(_, false))
177+
178+
_ <- l.countDown.asAsyncCallback
179+
_ <- l.pending.asAsyncCallback.tap(assertEq(_, 2))
180+
_ <- l.isComplete.asAsyncCallback.tap(assertEq(_, false))
181+
182+
_ <- l.countDown.asAsyncCallback
183+
_ <- l.pending.asAsyncCallback.tap(assertEq(_, 1))
184+
_ <- l.isComplete.asAsyncCallback.tap(assertEq(_, false))
185+
186+
_ <- AsyncCallback.delay { t = 5 }
187+
_ <- l.countDown.asAsyncCallback
188+
_ <- l.pending.asAsyncCallback.tap(assertEq(_, 0))
189+
_ <- l.isComplete.asAsyncCallback.tap(assertEq(_, true))
190+
_ <- l.await.map(_ => completedAt = t).fork_.asAsyncCallback
191+
_ <- b.await
192+
193+
} yield assertEq(completedAt, 5)
194+
}
195+
196+
"0" - asyncTest {
197+
for {
198+
l <- AsyncCallback.countDownLatch(0).asAsyncCallback
199+
_ <- l.pending.asAsyncCallback.tap(assertEq(_, 0))
200+
_ <- l.isComplete.asAsyncCallback.tap(assertEq(_, true))
201+
_ <- l.await
202+
} yield ()
203+
}
204+
}
205+
167206
}
168207
}

0 commit comments

Comments
 (0)