CINXE.COM

Execute Scala Futures in serial one after the other (non-blocking)

<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Execute Scala Futures in serial one after the other (non-blocking)</title> <meta name="viewport" content="width=device-width"> <link href="/assets/css/style.css" rel="stylesheet" /> <!-- favicon code start, by https://favicon.io/favicon-generator --> <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"> <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"> <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"> <link rel="manifest" href="/site.webmanifest"> <!-- favicon code end --> </head> <body> <div id="header-outer"> <header id="header"> <h1><a href="/">Michael Pollmeier ~ dev blog</a></h1> <nav> <ul> <li><a href="/">blog posts</a></li> <li><a href="/presentations">presentations</a></li> <li><a href="http://twitter.com/pollmeier">twitter</a></li> <li><a href="/feed.xml">rss</a></li> <li><a href="/about">about me</a></li> </ul> </nav> </header> </div> <div id="page"> <div id="content"> <article class="post"> <h1><a href="/execute-scala-futures-in-serial-one-after-the-other-non-blocking">Execute Scala Futures in serial one after the other (non-blocking)</a></h1> <p class="meta"> Posted on <span class="postdate">Aug 18, 2013</span><br/> tags: <!-- <a href="/tags.html#scala">scala</a> --> scala </p> <div class="post-content"><p>Scala’s Future implementation is really cool, it’s super easy to execute code in parallel, plus it’s very composable because it is a monad. As soon as you create a Future, it wanders off into a separate Thread and you eventually get the result:</p> <div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">val</span> <span class="nv">list</span> <span class="k">=</span> <span class="nc">List</span><span class="o">(</span><span class="mi">1</span><span class="o">,</span><span class="mi">2</span><span class="o">)</span> <span class="k">def</span> <span class="nf">doubleFuture</span><span class="o">(</span><span class="n">i</span><span class="k">:</span> <span class="kt">Int</span><span class="o">)</span> <span class="k">=</span> <span class="nc">Future</span> <span class="o">{</span> <span class="n">i</span> <span class="o">*</span> <span class="mi">2</span> <span class="o">}</span> <span class="n">list</span> <span class="n">map</span> <span class="n">doubleFuture</span> <span class="c1">//returns List[Future[Int]]</span> <span class="nv">Future</span><span class="o">.</span><span class="py">traverse</span><span class="o">(</span><span class="n">list</span><span class="o">)(</span><span class="n">doubleFuture</span><span class="o">)</span> <span class="c1">//returns Future[List[Int]]</span> </code></pre></div></div> <p>If we just <em>map</em> the list using our function, we get a list of Futures. More useful in this situation is to use <em>Future.traverse</em>, which essentially does the same but only returns a single Future. That one completes when all computations have been finished. Please note that both statements return instantly and execute all futures in parallel (that’s obviously a simplification as it depends on your Executor etc.).</p> <p>Sometimes if you have a cool new tool, new problems arise. Maybe you don’t want to execute everything in parallel every time? And maybe you want to stop the whole process if one of the computations failed? At work we had a couple of cases where we wanted to do exactly that: execute the futures one after the other, and stop if one fails. Obviously we did not want to use Await.result as that would block a precious thread. The trick is to fold over the list and use a for comprehension (alternatively flatMap) inside the fold, so that we only compute the next value once the previous has completed:</p> <div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">serialiseFutures</span><span class="o">[</span><span class="kt">A</span>, <span class="kt">B</span><span class="o">](</span><span class="n">l</span><span class="k">:</span> <span class="kt">Iterable</span><span class="o">[</span><span class="kt">A</span><span class="o">])(</span><span class="n">fn</span><span class="k">:</span> <span class="kt">A</span> <span class="k">⇒</span> <span class="kt">Future</span><span class="o">[</span><span class="kt">B</span><span class="o">])</span> <span class="o">(</span><span class="k">implicit</span> <span class="n">ec</span><span class="k">:</span> <span class="kt">ExecutionContext</span><span class="o">)</span><span class="k">:</span> <span class="kt">Future</span><span class="o">[</span><span class="kt">List</span><span class="o">[</span><span class="kt">B</span><span class="o">]]</span> <span class="k">=</span> <span class="nv">l</span><span class="o">.</span><span class="py">foldLeft</span><span class="o">(</span><span class="nc">Future</span><span class="o">(</span><span class="nv">List</span><span class="o">.</span><span class="py">empty</span><span class="o">[</span><span class="kt">B</span><span class="o">]))</span> <span class="o">{</span> <span class="o">(</span><span class="n">previousFuture</span><span class="o">,</span> <span class="n">next</span><span class="o">)</span> <span class="k">⇒</span> <span class="k">for</span> <span class="o">{</span> <span class="n">previousResults</span> <span class="k">←</span> <span class="n">previousFuture</span> <span class="n">next</span> <span class="k">←</span> <span class="nf">fn</span><span class="o">(</span><span class="n">next</span><span class="o">)</span> <span class="o">}</span> <span class="k">yield</span> <span class="n">previousResults</span> <span class="o">:+</span> <span class="n">next</span> <span class="o">}</span> </code></pre></div></div> <p>The fold passes the result of the previous computation into the current one (<em>previousFuture</em>). Using a for comprehension we only call our function once the previous future is complete (and successful). This also returns immediately, but the futures are actually executed one after the other - in serial.</p> <p>While that does the trick, there’s one downside to it: the return type is Future[List[B]]. Ideally it should return Future[C[B]] where C is the type of the collection we passed in. If you’ve read my <a href="http://www.michaelpollmeier.com/create-generic-scala-collections-with-canbuildfrom/">previous post</a> on CanBuildFrom then you know how you can do that:</p> <div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">serialiseFutures</span><span class="o">[</span><span class="kt">A</span>, <span class="kt">B</span>, <span class="kt">C</span><span class="o">[</span><span class="kt">A</span><span class="o">]</span> <span class="k">&lt;:</span> <span class="kt">Iterable</span><span class="o">[</span><span class="kt">A</span><span class="o">]]</span> <span class="o">(</span><span class="n">collection</span><span class="k">:</span> <span class="kt">C</span><span class="o">[</span><span class="kt">A</span><span class="o">])(</span><span class="n">fn</span><span class="k">:</span> <span class="kt">A</span> <span class="k">⇒</span> <span class="kt">Future</span><span class="o">[</span><span class="kt">B</span><span class="o">])(</span> <span class="k">implicit</span> <span class="n">ec</span><span class="k">:</span> <span class="kt">ExecutionContext</span><span class="o">,</span> <span class="n">cbf</span><span class="k">:</span> <span class="kt">CanBuildFrom</span><span class="o">[</span><span class="kt">C</span><span class="o">[</span><span class="kt">B</span><span class="o">]</span>, <span class="kt">B</span>, <span class="kt">C</span><span class="o">[</span><span class="kt">B</span><span class="o">]])</span><span class="k">:</span> <span class="kt">Future</span><span class="o">[</span><span class="kt">C</span><span class="o">[</span><span class="kt">B</span><span class="o">]]</span> <span class="k">=</span> <span class="o">{</span> <span class="k">val</span> <span class="nv">builder</span> <span class="k">=</span> <span class="nf">cbf</span><span class="o">()</span> <span class="nv">builder</span><span class="o">.</span><span class="py">sizeHint</span><span class="o">(</span><span class="nv">collection</span><span class="o">.</span><span class="py">size</span><span class="o">)</span> <span class="nv">collection</span><span class="o">.</span><span class="py">foldLeft</span><span class="o">(</span><span class="nc">Future</span><span class="o">(</span><span class="n">builder</span><span class="o">))</span> <span class="o">{</span> <span class="o">(</span><span class="n">previousFuture</span><span class="o">,</span> <span class="n">next</span><span class="o">)</span> <span class="k">⇒</span> <span class="k">for</span> <span class="o">{</span> <span class="n">previousResults</span> <span class="k">←</span> <span class="n">previousFuture</span> <span class="n">next</span> <span class="k">←</span> <span class="nf">fn</span><span class="o">(</span><span class="n">next</span><span class="o">)</span> <span class="o">}</span> <span class="k">yield</span> <span class="n">previousResults</span> <span class="o">+=</span> <span class="n">next</span> <span class="o">}</span> <span class="n">map</span> <span class="o">{</span> <span class="n">builder</span> <span class="k">⇒</span> <span class="nv">builder</span><span class="o">.</span><span class="py">result</span> <span class="o">}</span> <span class="o">}</span> </code></pre></div></div> <p>And here’s the ScalaTest code for it:</p> <div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">val</span> <span class="nv">start</span> <span class="k">=</span> <span class="nv">System</span><span class="o">.</span><span class="py">currentTimeMillis</span> <span class="k">val</span> <span class="nv">doubled</span> <span class="k">=</span> <span class="nv">Await</span><span class="o">.</span><span class="py">result</span><span class="o">({</span> <span class="nf">serialiseFutures</span><span class="o">(</span><span class="nc">List</span><span class="o">(</span><span class="mi">10</span><span class="o">,</span> <span class="mi">20</span><span class="o">))</span> <span class="o">{</span> <span class="n">i</span> <span class="k">⇒</span> <span class="nc">Future</span> <span class="o">{</span> <span class="nv">Thread</span><span class="o">.</span><span class="py">sleep</span><span class="o">(</span><span class="n">i</span><span class="o">)</span> <span class="n">i</span> <span class="o">*</span> <span class="mi">2</span> <span class="o">}</span> <span class="o">}</span> <span class="o">},</span> <span class="mi">1</span> <span class="n">second</span><span class="o">)</span> <span class="k">val</span> <span class="nv">timeElapsed</span> <span class="k">=</span> <span class="nv">System</span><span class="o">.</span><span class="py">currentTimeMillis</span> <span class="o">-</span> <span class="n">start</span> <span class="n">timeElapsed</span> <span class="n">should</span> <span class="n">be</span> <span class="o">&gt;=</span> <span class="o">(</span><span class="mi">30</span><span class="n">l</span><span class="o">)</span> <span class="n">doubled</span> <span class="n">should</span> <span class="nf">be</span><span class="o">(</span><span class="nc">List</span><span class="o">(</span><span class="mi">20</span><span class="o">,</span> <span class="mi">40</span><span class="o">))</span> </code></pre></div></div> <h1 id="questions">Questions</h1> <p>Q: Zoran Perosevic on 14/04: What is expected behavior in case of exceptions? Would the execution of subsequent futures stop if one throws an exception?</p> <p>A: In this implementation it would stop the execution if one future fails. That’s because of the nature of the for comprehension (which is using flatMap).</p> </div> <hr /> <div class="footer"> If you liked this post you may want to follow me on <a href="http://twitter.com/pollmeier">Twitter</a> and subscribe to the <a href="/feed.xml">RSS feed</a>. </div> </article> </div> </div> <script src="/assets/js/jquery.min.js"></script> <script src="/assets/js/jquery.mobilemenu.min.js"></script> <script> $(document).ready(function(){ $('#sidebar nav ul').mobileMenu({'topOptionText': 'Menu', 'prependTo': '#sidebar nav'}); }); </script> </body> </html>

Pages: 1 2 3 4 5 6 7 8 9 10