在scala中可以方便的实现异步操作,这里是通过Future来实现的,和java中的Future很相似,但是功能更加强大。

定义返回Future的方法

下面我们看下如何定义一个返回Future的方法:

println("Step 1: Define a method which returns a Future")
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
def donutStock(donut: String): Future[Int] = Future {
// assume some long running database operation
println("checking donut stock")
10
}

注意这里需要引入scala.concurrent.ExecutionContext.Implicits.global, 它会提供一个默认的线程池来异步执行Future。

阻塞方式获取Future的值

  println("\nStep 2: Call method which returns a Future")
import scala.concurrent.Await
import scala.concurrent.duration._
val vanillaDonutStock = Await.result(donutStock("vanilla donut"), 5 seconds)
println(s"Stock of vanilla donut = $vanillaDonutStock")

donutStock() 是异步执行的,我们可以使用Await.result() 来阻塞主线程来等待donutStock()的执行结果。

下面是其输出:


Step 2: Call method which returns a Future
checking donut stock
Stock of vanilla donut = 10

非阻塞方式获取Future的值

我们可以使用Future.onComplete() 回调来实现非阻塞的通知:

println("\nStep 2: Non blocking future result")
import scala.util.{Failure, Success}
donutStock("vanilla donut").onComplete {
case Success(stock) => println(s"Stock for vanilla donut = $stock")
case Failure(e) => println(s"Failed to find vanilla donut stock, exception = $e")
}
Thread.sleep(3000)

Future.onComplete() 有两种可能情况,Success 或者 Failure,需要引入: import scala.util.{Failure, Success}。

Future链

有时候我们需要在获得一个Future之后再继续对其进行操作,有点类似于java中的管道,下面看一个例子:

println("\nStep 2: Define another method which returns a Future")
def buyDonuts(quantity: Int): Future[Boolean] = Future {
println(s"buying $quantity donuts")
true
}

上面我们又定义了一个方法,用来接收donutStock()的返回值,然后再返回一个Future[Boolean] 。

我们看下使用flatmap该怎么链接他们:

println("\nStep 3: Chaining Futures using flatMap")
val buyingDonuts: Future[Boolean] = donutStock("plain donut").flatMap(qty => buyDonuts(qty))
import scala.concurrent.Await
import scala.concurrent.duration._
val isSuccess = Await.result(buyingDonuts, 5 seconds)
println(s"Buying vanilla donut was successful = $isSuccess")

同样的,我们还可以使用for语句来进行链接:


println("\nStep 3: Chaining Futures using for comprehension")
for {
stock <- donutStock("vanilla donut")
isSuccess <- buyDonuts(stock)
} yield println(s"Buying vanilla donut was successful = $isSuccess") Thread.sleep(3000)

flatmap VS map

map就是对集合中的元素进行重映射,而flatmap则会将返回的值拆散然后重新组合。 下面举个直观的例子:

val buyingDonuts: Future[Boolean] = donutStock("plain donut").flatMap(qty => buyDonuts(qty))

flatMap返回的值是Future[Boolean]。

val buyingDonuts: Future[Future[Boolean]] = donutStock("plain donut").Map(qty => buyDonuts(qty))

map返回的值是Future[Future[Boolean]]。

Future.sequence() VS Future.traverse()

如果我们有很多个Future,然后想让他们并行执行,则可以使用 Future.sequence() 。

println(s"\nStep 2: Create a List of future operations")
val futureOperations = List(
donutStock("vanilla donut"),
donutStock("plain donut"),
donutStock("chocolate donut")
) println(s"\nStep 5: Call Future.sequence to run the future operations in parallel")
val futureSequenceResults = Future.sequence(futureOperations)
futureSequenceResults.onComplete {
case Success(results) => println(s"Results $results")
case Failure(e) => println(s"Error processing future operations, error = ${e.getMessage}")
}

Future.traverse() 和Future.sequence() 类似, 唯一不同的是,Future.traverse()可以对要执行的Future进行操作,如下所示:

println(s"\nStep 3: Call Future.traverse to convert all Option of Int into Int")
val futureTraverseResult = Future.traverse(futureOperations){ futureSomeQty =>
futureSomeQty.map(someQty => someQty.getOrElse(0))
}
futureTraverseResult.onComplete {
case Success(results) => println(s"Results $results")
case Failure(e) => println(s"Error processing future operations, error = ${e.getMessage}")
}

Future.foldLeft VS Future reduceLeft

foldLeft 和 reduceLeft 都是用来从左到右做集合操作的,区别在于foldLeft可以提供默认值。看下下面的例子:

println(s"\nStep 3: Call Future.foldLeft to fold over futures results from left to right")
val futureFoldLeft = Future.foldLeft(futureOperations)(0){ case (acc, someQty) =>
acc + someQty.getOrElse(0)
}
futureFoldLeft.onComplete {
case Success(results) => println(s"Results $results")
case Failure(e) => println(s"Error processing future operations, error = ${e.getMessage}")
}

输出结果:


Step 3: Call Future.foldLeft to fold over futures results from left to right
Results 20
println(s"\nStep 3: Call Future.reduceLeft to fold over futures results from left to right")
val futureFoldLeft = Future.reduceLeft(futureOperations){ case (acc, someQty) =>
acc.map(qty => qty + someQty.getOrElse(0))
}
futureFoldLeft.onComplete {
case Success(results) => println(s"Results $results")
case Failure(e) => println(s"Error processing future operations, error = ${e.getMessage}")
}

输出结果:

Step 3: Call Future.reduceLeft to fold over futures results from left to right
Results Some(20)

Future firstCompletedOf

firstCompletedOf在处理多个Future请求时,会返回第一个处理完成的future结果。

println(s"\nStep 3: Call Future.firstCompletedOf to get the results of the first future that completes")
val futureFirstCompletedResult = Future.firstCompletedOf(futureOperations)
futureFirstCompletedResult.onComplete {
case Success(results) => println(s"Results $results")
case Failure(e) => println(s"Error processing future operations, error = ${e.getMessage}")
}

Future zip VS zipWith

zip用来将两个future结果组合成一个tuple. zipWith则可以自定义Function来处理future返回的结果。

println(s"\nStep 3: Zip the values of the first future with the second future")
val donutStockAndPriceOperation = donutStock("vanilla donut") zip donutPrice()
donutStockAndPriceOperation.onComplete {
case Success(results) => println(s"Results $results")
case Failure(e) => println(s"Error processing future operations, error = ${e.getMessage}")
}

输出值:


Step 3: Zip the values of the first future with the second future
checking donut stock
Results (Some(10),3.25)

使用zipwith的例子:

println(s"\nStep 4: Call Future.zipWith and pass-through function qtyAndPriceF")
val donutAndPriceOperation = donutStock("vanilla donut").zipWith(donutPrice())(qtyAndPriceF)
donutAndPriceOperation.onComplete {
case Success(result) => println(s"Result $result")
case Failure(e) => println(s"Error processing future operations, error = ${e.getMessage}")
}

输出结果:

Step 4: Call Future.zipWith and pass-through function qtyAndPriceF
checking donut stock
Result (10,3.25)

Future andThen

andThen后面可以跟一个自定义的PartialFunction,来处理Future返回的结果, 如下所示:

println(s"\nStep 2: Call Future.andThen with a PartialFunction")
val donutStockOperation = donutStock("vanilla donut")
donutStockOperation.andThen { case stockQty => println(s"Donut stock qty = $stockQty")}

输出结果:

Step 2: Call Future.andThen with a PartialFunction
checking donut stock
Donut stock qty = Success(10)

自定义threadpool

上面的例子中, 我们都是使用了scala的全局ExecutionContext: scala.concurrent.ExecutionContext.Implicits.global.

同样的,我们也可以自定义你自己的ExecutionContext。下面是一个使用java.util.concurrent.Executors的例子:

  println("Step 1: Define an ExecutionContext")
val executor = Executors.newSingleThreadExecutor()
implicit val ec = scala.concurrent.ExecutionContext.fromExecutor(executor) println("\nStep 2: Define a method which returns a Future")
import scala.concurrent.Future
def donutStock(donut: String): Future[Int] = Future {
// assume some long running database operation
println("checking donut stock")
10
} println("\nStep 3: Call method which returns a Future")
val donutStockOperation = donutStock("vanilla donut")
donutStockOperation.onComplete {
case Success(donutStock) => println(s"Results $donutStock")
case Failure(e) => println(s"Error processing future operations, error = ${e.getMessage}")
} Thread.sleep(3000)
executor.shutdownNow()

recover() recoverWith() and fallbackTo()

这三个方法主要用来处理异常的,recover是用来从你已知的异常中恢复,如下所示:

println("\nStep 3: Call Future.recover to recover from a known exception")
donutStock("unknown donut")
.recover { case e: IllegalStateException if e.getMessage == "Out of stock" => 0 }
.onComplete {
case Success(donutStock) => println(s"Results $donutStock")
case Failure(e) => println(s"Error processing future operations, error = ${e.getMessage}")
}

recoverWith()和recover()类似,不同的是他的返回值是一个Future。

println("\nStep 3: Call Future.recoverWith to recover from a known exception")
donutStock("unknown donut")
.recoverWith { case e: IllegalStateException if e.getMessage == "Out of stock" => Future.successful(0) }
.onComplete {
case Success(donutStock) => println(s"Results $donutStock")
case Failure(e) => println(s"Error processing future operations, error = ${e.getMessage}")
}

fallbackTo()是在发生异常时,去调用指定的方法:

println("\nStep 3: Call Future.fallbackTo")
val donutStockOperation = donutStock("plain donut")
.fallbackTo(similarDonutStock("vanilla donut"))
.onComplete {
case Success(donutStock) => println(s"Results $donutStock")
case Failure(e) => println(s"Error processing future operations, error = ${e.getMessage}")
}

promise

熟悉ES6的同学可能知道,promise是JS在ES6中引入的新特性,其主要目的是将回调转变成链式调动。

当然scala的promise和ES6的promise还是不一样的,我们看下scala中promise是怎么用的:

  println("Step 1: Define a method which returns a Future")
import scala.concurrent.ExecutionContext.Implicits.global
def donutStock(donut: String): Int = {
if(donut == "vanilla donut") 10
else throw new IllegalStateException("Out of stock")
} println(s"\nStep 2: Define a Promise of type Int")
val donutStockPromise = Promise[Int]() println("\nStep 3: Define a future from Promise")
val donutStockFuture = donutStockPromise.future
donutStockFuture.onComplete {
case Success(stock) => println(s"Stock for vanilla donut = $stock")
case Failure(e) => println(s"Failed to find vanilla donut stock, exception = $e")
} println("\nStep 4: Use Promise.success or Promise.failure to control execution of your future")
val donut = "vanilla donut"
if(donut == "vanilla donut") {
donutStockPromise.success(donutStock(donut))
} else {
donutStockPromise.failure(Try(donutStock(donut)).failed.get)
} println("\nStep 5: Completing Promise using Promise.complete() method")
val donutStockPromise2 = Promise[Int]()
val donutStockFuture2 = donutStockPromise2.future
donutStockFuture2.onComplete {
case Success(stock) => println(s"Stock for vanilla donut = $stock")
case Failure(e) => println(s"Failed to find vanilla donut stock, exception = $e")
}
donutStockPromise2.complete(Try(donutStock("unknown donut")))

上面例子中我们使用了 Promise.success, Promise.failure, Promise.complete() 来控制程序的运行。

更多教程请参考 flydean的博客

Scala教程之:Future和Promise的更多相关文章

  1. scala教程之:可见性规则

    文章目录 public Protected private scoped private 和 scoped protected 和java很类似,scala也有自己的可见性规则,不同的是scala只有 ...

  2. Scala教程之:深入理解协变和逆变

    文章目录 函数的参数和返回值 可变类型的变异 在之前的文章中我们简单的介绍过scala中的协变和逆变,我们使用+ 来表示协变类型:使用-表示逆变类型:非转化类型不需要添加标记. 假如我们定义一个cla ...

  3. Scala教程之:Either

    在之前的文章中我们提到了Option,scala中Option表示存在0或者1个元素,如果在处理异常的时候Option就会有很大的限制,因为Option如果返回None,那么我并不知道具体的异常到底是 ...

  4. Scala教程之:可变和不变集合

    文章目录 mutable HashMap immutable HashMap 集合在程序中是非常有用的,只有用好集合才能真正感受到该语言的魅力.在scala中集合主要在三个包里面:scala.coll ...

  5. Scala教程之:PartialFunction

    Scala中有一个很有用的traits叫PartialFunction,我看了下别人的翻译叫做偏函数,但是我觉得部分函数更加确切. 那么PartialFunction是做什么用的呢?简单点说Parti ...

  6. Scala教程之:Enumeration

    Enumeration应该算是程序语言里面比较通用的一个类型,在scala中也存在这样的类型, 我们看下Enumeration的定义: abstract class Enumeration (init ...

  7. Scala教程之:Option-Some-None

    文章目录 Option和Some Option和None Option和模式匹配 在java 8中,为了避免NullPointerException,引入了Option,在Scala中也有同样的用法. ...

  8. Scala教程之:scala的参数

    文章目录 默认参数值 命名参数 scala的参数有两大特点: 默认参数值 命名参数 默认参数值 在Scala中,可以给参数提供默认值,这样在调用的时候可以忽略这些具有默认值的参数. def log(m ...

  9. Scala教程之:可扩展的scala

    文章目录 隐式类 限制条件 字符串插值 s 字符串插值器 f 插值器 raw 插值器 自定义插值器 Scala是扩展的,Scala提供了一种独特的语言机制来实现这种功能: 隐式类: 允许给已有的类型添 ...

随机推荐

  1. EOS基础全家桶(六)账号管理

    简介 本篇我们会学习最基本的账号相关的操作,包括了创建账号和查询,关于账号资源的操作因为必须先部署系统合约,所以我们会留到后面单独写一篇来讲解. 6-EOS基础全家桶(六)账号管理 简介 账号介绍 账 ...

  2. 关于github报错:ssh: connect to host github.com port 22: Connection timed outfatal: Could not read from remote repository.Please make sure you have the correct access rightsand the repository exists.

    当执行git命令如:git clone.git pull等等 出现报错:ssh: connect to host github.com port 22: Connection timed outfat ...

  3. 简单分析ucenter 会员同步登录通信原理

    1.用户登录discuz,通过logging.php文件中的函数uc_user_login对post过来的数据进行验证,也就是对username和password进行验证. 2.如果验证成功,将调用位 ...

  4. Python库-Matplotlib

    Matplotlib官网https://matplotlib.org,Matplotlib是一个Python的2D绘图库. 可视化是整个数据分析的关键辅助工具,可以清晰的理解数据. 折线图(用于显示数 ...

  5. 28.4 Calendar 日历

    /* * Calendar:日历,提供了一些操作年月日时的方法 * 获取 * 修改 * 添加 */ public class CalendarDemo { public static void mai ...

  6. 多线程高并发编程(3) -- ReentrantLock源码分析AQS

    背景: AbstractQueuedSynchronizer(AQS) public abstract class AbstractQueuedSynchronizer extends Abstrac ...

  7. Python入门学习指导(VS Code配置向)

    代码编辑器或IDE 推荐Vs Code,Atom和Sublime(本文以Vs Code为例,Sublime对中文支持不是很好,时常弄好了Sublime的乱码,却在复制到其他编辑器时出了问题) Vs C ...

  8. hibernate.current_session_context_class 比较权威的解释

    hibernate.current_session_context_class 博客分类: hibernate HibernateSpring多线程配置管理thread  遇到过的问题: 情景1: 在 ...

  9. AJ学IOS(48)多线程网络之多线程简单了解

    AJ分享,必须精品 一:进程和线程 1:什么是进程 进程是指在系统中正在运行的一个应用程序 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内. 比如同时打开QQ.Xcode,系统就会分 ...

  10. work of 12/30/2015

    part 组员                今日工作              工作耗时/h 明日计划 工作耗时/h    UI 冯晓云   merge UI 与reader     6  丰富re ...