简体   繁体   中英

What's the difference between the output of the following concurrent Scala programs

// First import concurrent.Future import concurrent.ExecutionContext.Implicits.global for { _ <- Future { Thread.sleep(3000); println("a") } _ <- Future { Thread.sleep(2000); println("b") } _ <- Future { Thread.sleep(1000); println("c") } } {}
 // Second 
import concurrent.Future import concurrent.ExecutionContext.Implicits.global val future1 = Future { Thread.sleep(3000); println("a") } val future2 = Future { Thread.sleep(2000); println("b") } val future3 = Future { Thread.sleep(1000); println("c") } for { _ <- future1 _ <- future2 _ <- future3 } {}


You can use the command below to have a better understanding of what Scala compiler do under the hood:

$ scalac -Xprint:typer MainClass.scala

'First' will be desugared into:


    scala.concurrent.Future.apply[Unit]({
      java.lang.Thread.sleep(3000L); scala.Predef.println("a")
    })(scala.concurrent.ExecutionContext.Implicits.global)
      .foreach[Unit](((_: Unit) =>
      scala.concurrent.Future.apply[Unit]({
        java.lang.Thread.sleep(2000L); scala.Predef.println("b")
      })(scala.concurrent.ExecutionContext.Implicits.global)
        .foreach[Unit](((_: Unit) => scala.concurrent.Future.apply[Unit]({
        java.lang.Thread.sleep(1000L); scala.Predef.println("c")
      })(scala.concurrent.ExecutionContext.Implicits.global)
        .foreach[Unit](((_: Unit) => ()))(scala.concurrent.ExecutionContext.Implicits.global)))(scala.concurrent.ExecutionContext.Implicits.global)))
    (scala.concurrent.ExecutionContext.Implicits.global);

'Second' into

    val future1: scala.concurrent.Future[Unit] = scala.concurrent.Future.apply[Unit]({
      java.lang.Thread.sleep(3000L);
      scala.Predef.println("a")
    })(scala.concurrent.ExecutionContext.Implicits.global);
    val future2: scala.concurrent.Future[Unit] = scala.concurrent.Future.apply[Unit]({
      java.lang.Thread.sleep(2000L);
      scala.Predef.println("b")
    })(scala.concurrent.ExecutionContext.Implicits.global);
    val future3: scala.concurrent.Future[Unit] = scala.concurrent.Future.apply[Unit]({
      java.lang.Thread.sleep(1000L);
      scala.Predef.println("c")
    })(scala.concurrent.ExecutionContext.Implicits.global);
    {
      future1.flatMap[Unit](((_: Unit) => future2.flatMap[Unit](((_: Unit) => future3.map[Unit](((_: Unit) => ()))(scala.concurrent.ExecutionContext.Implicits.global)))(scala.concurrent.ExecutionContext.Implicits.global)))(scala.concurrent.ExecutionContext.Implicits.global);
      ()
    }

In 'First' case the next Future will be created inside the '.foreach' of the first Future and so on.

In 'Second' case all 3 futures will be created first, executed in parallel and then flatMap'ped.

Since the for expression is desugared to a series of nested flatMap / map calls, the Future instances in the first examples will run sequentially.

While the code in the second example will, depending on the ExecutionContext in scope, run the Future instances in parallel.

Another thing to keep in mind is that scala Futures are strict, which means that you can't separate Future definition from its execution. You can read about this and other Future weaknesses here:

Scala Futures vs Monix Tasks

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM