Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>If you don't mind a very localised <code>var</code>, you can serialise the asynchronous processing (each <code>f(item)</code>) as follows (<code>flatMap</code> does the serialization):</p> <pre><code>val fSerialized = { var fAccum = Future{()} for(item &lt;- it) { println(s"Processing ${item}") fAccum = fAccum flatMap { _ =&gt; f(item) } } fAccum } fSerialized.onComplete{case resTry =&gt; println("All Done.")} </code></pre> <p>In general, avoid <code>Await</code> operations - they block (kind of defeats the point of async, consumes resources and for sloppy designs, can deadlock)</p> <hr> <p><strong>Cool Trick 1:</strong></p> <p>You can chain together <code>Futures</code> via that usual suspect, <code>flatmap</code> - it serializes asynchronous operations. Is there anything it can't do? ;-)</p> <pre><code>def f1 = Future { // some background running logic here...} def f2 = Future { // other background running logic here...} val fSerialized: Future[Unit] = f1 flatMap(res1 =&gt; f2) fSerialized.onComplete{case resTry =&gt; println("Both Done: Success=" + resTry.isSuccess)} </code></pre> <p>None of the above blocks - the main thread runs straight through in a few dozen nanoseconds. Futures are used in all cases to execute parallel threads and keep track of asynchronous state/results and to chain logic.</p> <p><code>fSerialized</code> represents a composite of two different asynchronous operations chained together. As soon as the val is evaluated, it immediately starts <code>f1</code> (running asynchonously). <code>f1</code> runs like any <code>Future</code> - when it eventually finishes, it calls it's <code>onComplete</code> callback block. Here's the cool bit - <code>flatMap</code> installs it's argument as the <code>f1</code> onComplete callback block - so <code>f2</code> is initiated as soon as <code>f1</code> completes, with no blocking, polling or wasteful resource usage. When <code>f2</code> is complete, then <code>fSerialized</code> is complete - so it runs the <code>fSerialized.onComplete</code> callback block - printing "Both Done".</p> <p>Not only that, but you can chain flatmaps as much as you like with neat non-spaghetti code</p> <pre><code> f1 flatmap(res1 =&gt; f2) flatMap(res2 =&gt; f3) flatMap(res3 =&gt; f4) ... </code></pre> <p>If you were to do that via Future.onComplete, you would have to embed the successive operations as nested onComplete layers:</p> <pre><code>f1.onComplete{case res1Try =&gt; f2 f2.onComplete{case res2Try =&gt; f3 f3.onComplete{case res3Try =&gt; f4 f4.onComplete{ ... } } } } </code></pre> <p>Not as nice.</p> <p>Test to prove:</p> <pre><code>import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.blocking import scala.concurrent.duration._ def f(item: Int): Future[Unit] = Future{ print("Waiting " + item + " seconds ...") Console.flush blocking{Thread.sleep((item seconds).toMillis)} println("Done") } val fSerial = f(4) flatMap(res1 =&gt; f(16)) flatMap(res2 =&gt; f(2)) flatMap(res3 =&gt; f(8)) fSerial.onComplete{case resTry =&gt; println("!!!! That's a wrap !!!! Success=" + resTry.isSuccess)} </code></pre> <p><strong>Cool Trick 2:</strong></p> <p>for-comprehensions like this:</p> <pre><code>for {a &lt;- aExpr; b &lt;- bExpr; c &lt;- cExpr; d &lt;- dExpr} yield eExpr </code></pre> <p>are nothing but syntactic-sugar for this: </p> <pre><code>aExpr.flatMap{a =&gt; bExpr.flatMap{b =&gt; cExpr.flatMap{c =&gt; dExpr.map{d =&gt; eExpr} } } } </code></pre> <p>that's a chain of flatMaps, followed by a final map.</p> <p>That means that</p> <pre><code>f1 flatmap(res1 =&gt; f2) flatMap(res2 =&gt; f3) flatMap(res3 =&gt; f4) map(res4 =&gt; "Did It!") </code></pre> <p>is identical to</p> <pre><code>for {res1 &lt;- f1; res2 &lt;- f2; res3 &lt;- f3; res4 &lt;- f4} yield "Did It!" </code></pre> <p>Test to Prove (following on from previous test):</p> <pre><code>val fSerial = for {res1 &lt;- f(4); res2 &lt;- f(16); res3 &lt;- f(2); res4 &lt;- f(8)} yield "Did It!" fSerial.onComplete{case resTry =&gt; println("!!!! That's a wrap !!!! Success=" + resTry.isSuccess)} </code></pre> <p><strong>Not-So-Cool Trick 3:</strong></p> <p>Unfortunately you can't mix iterators &amp; futures in the same for-comprehension. Compile error: </p> <pre><code>val fSerial = {for {nextItem &lt;- itemIterable; nextRes &lt;- f(nextItem)} yield "Did It"}.last </code></pre> <p>And nesting fors creates a challenge. The following doesn't serialize, but runs async blocks in parallel (nested comprehensions don't chain subsequent Futures with flatMap/Map, but instead chains as Iterable.flatMap{item => f(item)} - not the same!)</p> <pre><code>val fSerial = {for {nextItem &lt;- itemIterable} yield for {nextRes &lt;- f(nextItem)} yield "Did It"}.last </code></pre> <p>Also using foldLeft/foldRight plus flatMap doesn't work as you'd expect - seems a bug/limitation; all async blocks are processed in parallel (so <code>Iterator.foldLeft/Right</code> is not sociable with <code>Future.flatMap</code>):</p> <pre><code>import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.blocking import scala.concurrent.duration._ def f(item: Int): Future[Unit] = Future{ print("Waiting " + item + " seconds ...") Console.flush blocking{Thread.sleep((item seconds).toMillis)} println("Done") } val itemIterable: Iterable[Int] = List[Int](4, 16, 2, 8) val empty = Future[Unit]{()} def serialize(f1: Future[Unit], f2: Future[Unit]) = f1 flatMap(res1 =&gt; f2) //val fSerialized = itemIterable.iterator.foldLeft(empty){(fAccum, item) =&gt; serialize(fAccum, f(item))} val fSerialized = itemIterable.iterator.foldRight(empty){(item, fAccum) =&gt; serialize(fAccum, f(item))} fSerialized.onComplete{case resTry =&gt; println("!!!! That's a wrap !!!! Success=" + resTry.isSuccess)} </code></pre> <p>But this works (var involved):</p> <pre><code>import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.blocking import scala.concurrent.duration._ def f(item: Int): Future[Unit] = Future{ print("Waiting " + item + " seconds ...") Console.flush blocking{Thread.sleep((item seconds).toMillis)} println("Done") } val itemIterable: Iterable[Int] = List[Int](4, 16, 2, 8) var fSerial = Future{()} for {nextItem &lt;- itemIterable} fSerial = fSerial.flatMap(accumRes =&gt; f(nextItem)) </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload