@@ -2,7 +2,7 @@ package japgolly.scalajs.react
22
33import org .scalajs .dom .console
44import scala .annotation .implicitNotFound
5- import scala .concurrent .{Future , Promise }
5+ import scala .concurrent .{ExecutionContext , Future , Promise }
66import scala .concurrent .duration .{FiniteDuration , MILLISECONDS }
77import scala .scalajs .js
88import js .{undefined , UndefOr , Function0 => JFn0 , Function1 => JFn1 }
@@ -52,6 +52,17 @@ object Callback {
5252 @ inline def byName (f : => Callback ): Callback =
5353 CallbackTo (f.runNow())
5454
55+ /**
56+ * Wraps a [[Future ]] so that it is repeatable, and so that its inner callback is run when the future completes.
57+ *
58+ * The result is discarded. To retain it, use [[CallbackTo.future) ]] instead.
59+ *
60+ * WARNING: Futures are scheduled to run as soon as they're created. Ensure that the argument you provide creates a
61+ * new [[Future ]]; don't reference an existing one.
62+ */
63+ def future [A ](f : => Future [CallbackTo [A ]])(implicit ec : ExecutionContext ): Callback =
64+ CallbackTo .future(f).voidExplicit[Future [A ]]
65+
5566 /**
5667 * Convenience for applying a condition to a callback, and returning `Callback.empty` when the condition isn't
5768 * satisfied.
@@ -143,6 +154,15 @@ object CallbackTo {
143154 @ inline def byName [A ](f : => CallbackTo [A ]): CallbackTo [A ] =
144155 CallbackTo (f.runNow())
145156
157+ /**
158+ * Wraps a [[Future ]] so that it is repeatable, and so that its inner callback is run when the future completes.
159+ *
160+ * WARNING: Futures are scheduled to run as soon as they're created. Ensure that the argument you provide creates a
161+ * new [[Future ]]; don't reference an existing one.
162+ */
163+ def future [A ](f : => Future [CallbackTo [A ]])(implicit ec : ExecutionContext ): CallbackTo [Future [A ]] =
164+ CallbackTo (f.map(_.runNow()))
165+
146166 /**
147167 * Serves as a temporary placeholder for a callback until you supply a real implementation.
148168 *
@@ -164,6 +184,18 @@ object CallbackTo {
164184 @ deprecated(" " , " not really deprecated" )
165185 def TODO [A ](result : => A , reason : => String ): CallbackTo [A ] =
166186 Callback .todoImpl(Some (() => reason)) >> CallbackTo (result)
187+
188+ final class ReactExt_CallbackToFuture [A ](private val _c : () => Future [A ]) extends AnyVal {
189+ @ inline private def c = new CallbackTo (_c)
190+
191+ /**
192+ * Turns a `CallbackTo[Future[A]]` into a `Future[A]`.
193+ *
194+ * WARNING: This will trigger the execution of the [[Callback ]].
195+ */
196+ def toFlatFuture (implicit ec : ExecutionContext ): Future [A ] =
197+ c.toFuture.flatMap(identity)
198+ }
167199}
168200
169201// =====================================================================================================================
@@ -268,6 +300,14 @@ final class CallbackTo[A] private[react] (private[CallbackTo] val f: () => A) ex
268300 def void : Callback =
269301 ret(())
270302
303+ /**
304+ * Discard the value produced by this callback.
305+ *
306+ * This method allows you to be explicit about the type you're discarding (which may change in future).
307+ */
308+ @ inline def voidExplicit [B ](implicit ev : A =:= B ): Callback =
309+ void
310+
271311 def conditionally (cond : => Boolean ): CallbackTo [Option [A ]] =
272312 CallbackTo (if (cond) Some (f()) else None )
273313
@@ -394,6 +434,12 @@ final class CallbackTo[A] private[react] (private[CallbackTo] val f: () => A) ex
394434 p.future
395435 }
396436
437+ /**
438+ * Schedules an instance of this callback to run asynchronously.
439+ */
440+ def toFuture (implicit ec : ExecutionContext ): Future [A ] =
441+ Future (runNow())
442+
397443 /**
398444 * Record the duration of this callback's execution.
399445 */
@@ -459,7 +505,7 @@ final class CallbackTo[A] private[react] (private[CallbackTo] val f: () => A) ex
459505 bool2(b)(_() || _())
460506
461507 /**
462- * Negates the callback result (so long as its boolean).
508+ * Negates the callback result (so long as it's boolean).
463509 */
464510 def ! (implicit ev : ThisIsBool ): CallbackB =
465511 ev(this ).map(! _)
0 commit comments