Skip to content

Commit 26f9d0c

Browse files
committed
Add AsyncCallback.awaitAll
Closes #807
1 parent f33f229 commit 26f9d0c

File tree

3 files changed

+64
-4
lines changed

3 files changed

+64
-4
lines changed

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

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,22 @@ object AsyncCallback {
104104
def pure[A](a: A): AsyncCallback[A] =
105105
const(Success(a))
106106

107-
def throwException[A](t: Throwable): AsyncCallback[A] =
108-
const(Failure(t))
107+
def throwException[A](t: => Throwable): AsyncCallback[A] =
108+
const {
109+
try
110+
Failure(t)
111+
catch {
112+
case t2: Throwable => Failure(t2)
113+
}
114+
}
115+
116+
def throwExceptionWhenDefined(o: => Option[Throwable]): AsyncCallback[Unit] =
117+
byName {
118+
o match {
119+
case None => unit
120+
case Some(t) => throwException(t)
121+
}
122+
}
109123

110124
def const[A](t: Try[A]): AsyncCallback[A] =
111125
AsyncCallback(_(t))
@@ -182,14 +196,24 @@ object AsyncCallback {
182196
case 0 => AsyncCallback.unit
183197
case 1 => AsyncCallback.byName(f(as(0))).void
184198
case n =>
199+
var error = Option.empty[Throwable]
185200
val latch = countDownLatch(n).runNow()
186201

202+
def onTaskComplete(r: Try[B]): Callback =
203+
Callback {
204+
r match {
205+
case Success(_) =>
206+
case Failure(e) => error = Some(e)
207+
}
208+
} >> latch.countDown
209+
187210
for (a <- as)
188211
AsyncCallback.byName(f(a))
189-
.finallyRunSync(latch.countDown)
212+
.attemptTry
213+
.flatMapSync(onTaskComplete)
190214
.runNow()
191215

192-
latch.await
216+
latch.await >> throwExceptionWhenDefined(error)
193217
}
194218
}
195219

@@ -290,6 +314,12 @@ object AsyncCallback {
290314
}
291315

292316
final case class Forked[A](await: AsyncCallback[A], isComplete: CallbackTo[Boolean])
317+
318+
def awaitAll(as: AsyncCallback[_]*): AsyncCallback[Unit] =
319+
if (as.isEmpty)
320+
unit
321+
else
322+
sequence_(as.iterator.asInstanceOf[Iterator[AsyncCallback[Any]]])
293323
}
294324

295325
/** Pure asynchronous callback.

doc/changelog/1.7.7.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ This entire release is focused on `AsyncCallback`.
1313

1414
* `AsyncCallback` object:
1515

16+
* Added `def awaitAll(as: AsyncCallback[_]*): AsyncCallback[Unit]` to wait for a number of async processes to complete.
17+
1618
* Added `countDownLatch(count: Int)` which returns an `AsyncCallback.CountDownLatch`.
1719
It has the same purpose and semantics as Java's `CountDownLatch`.
1820

@@ -32,6 +34,10 @@ This entire release is focused on `AsyncCallback`.
3234
* `traverseOption`
3335
* `sequenceOption`
3436

37+
* The argument to `throwException` is now by-name
38+
39+
* Added `def throwExceptionWhenDefined(o: => Option[Throwable]): AsyncCallback[Unit]`
40+
3541
* `AsyncCallback.Barrier`:
3642

3743
* 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: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,5 +203,29 @@ object AsyncCallbackTest extends TestSuite {
203203
}
204204
}
205205

206+
"awaitAll" - {
207+
"ok" - asyncTest {
208+
for {
209+
b1 <- AsyncCallback.barrier.asAsyncCallback
210+
b2 <- AsyncCallback.barrier.asAsyncCallback
211+
f <- AsyncCallback.awaitAll(b1.await.ret(1), b2.await.ret("a")).fork.asAsyncCallback
212+
_ <- f.isComplete.asAsyncCallback.tap(assertEq(_, false))
213+
_ <- b1.complete.asAsyncCallback
214+
_ <- f.isComplete.asAsyncCallback.tap(assertEq(_, false))
215+
_ <- b2.complete.asAsyncCallback
216+
_ <- f.await
217+
_ <- f.isComplete.asAsyncCallback.tap(assertEq(_, true))
218+
} yield ()
219+
}
220+
221+
"ko" - asyncTest {
222+
val b1 = AsyncCallback.unit
223+
val b2 = AsyncCallback.throwException(new RuntimeException("yep")).delayMs(1)
224+
for {
225+
t <- AsyncCallback.awaitAll(b1, b2).attemptTry
226+
} yield assert(t.isFailure)
227+
}
228+
}
229+
206230
}
207231
}

0 commit comments

Comments
 (0)