据我了解,在Scala中有3种执行IO的方法,我将尝试用伪代码来表达。
一,同步与阻塞:
val syncAndBlocking: HttpResponse = someHttpClient.get("foo.com")
在这里,主线程只是空闲,直到响应返回。
其次,异步但仍处于阻塞状态:
val asyncButBlocking: Future[HttpResponse] = Future { someHttpClient.get("bar.com") }
据我了解,这里的主线程是空闲的(因为Future在单独的线程上执行),但是该单独的线程被阻塞了。
第三,异步和非阻塞。我不确定如何实现该功能,但我最好猜想该实现(例如,http客户端)本身必须是非阻塞的,因此类似:
val asynAndNotBlocking: Future[HttpResponse] = Future { someNonBlockingHttpClient.get("baz.com") }
我的问题是:
val syncAndBlocking: HttpResponse = someHttpClient.get("foo.com")
这将阻塞调用线程,直到收到响应为止(如您所述)。
val asyncButBlocking: Future[HttpResponse] = Future { someHttpClient.get("bar.com") }
与对的任何调用一样Future.apply
(至少在概念上,可能有一些优化可以消除某些步骤):
Function0[HttpResponse]
(我称之为一个thunk,为了简洁),其apply
方法someHttpClient.get("bar.com")
。如果someHttpClient
是静态的,那么理论上这可能会在编译时发生(我不知道Scala编译器是否会执行此优化),否则会在运行时发生。Future.apply
,然后:Promise[HttpResponse]
。ExecutionContext
传递的任务隐式调度到Future.apply
。这个任务就是调用thunk:如果thunk成功执行,则与th的结果将成功完成Promise
的关联Future
,否则,throw会Future
失败(也将是完成)Throwable
(仅当Throwable
匹配时匹配才能失败)通过NonFatal
,我太懒了,无法检查,在这种情况下,致命的错误会被捕获ExecutionContext
。ExecutionContext
(执行重击之前可能会或可能不会安排任务),就会返回与Future
关联的任务Promise
。如何执行thunk的细节取决于ExecutionContext
Scala运行时的细节(对于扩展,取决于Scala运行时的细节)(对于JVM上的Scala,thunk将在所确定的线程上运行ExecutionContext
,无论是OS线程还是绿色线程)线程依赖于JVM,但是OS线程可能至少现在是一个安全的假设(Project Loom可能会影响到这一点);对于ScalaJS(因为JavaScript不公开线程)和Scala Native(据我所知:)一个ExecutionContext
可以利用操作系统的线程,但会在周围的事物,喜欢GC运行风险),这可能是一个事件循环与全局线程)。
在执行步骤5之前,调用线程一直处于阻塞状态,因此从调用者的角度来看,它是非阻塞的,但是某个地方有一个线程被阻塞。
val asynAndNotBlocking: Future[HttpResponse] = Future { someNonBlockingHttpClient.get("baz.com") }
......是可能不会类型检查(假设它是相同的HttpResponse
类型如以上),因为以无阻塞的HttpResponse
将不得不被包裹在其表示异步/不阻断(例如,类型Future
),所以asyncAndNotBlocking
是type Future[Future[HttpResponse]]
,在一些特定用例之外是一种无意义的类型。您更有可能会遇到以下情况:
val asyncAndNotBlocking: Future[HttpResponse] = someNonBlockingHttpClient.get("baz.com")
或者,如果someNonBlockingHttpClient
不是Scala的本机,并且返回其他一些异步库,您将拥有
val asyncAndNotBlocking: Future[HttpResponse] = SomeConversionToFuture(someNonBlockingHttpClient.get("baz.com"))
SomeConversionToFuture
基本上类似于上面的草图Future.apply
,但是可以代替ExecutionContext
在其他异步库中使用use操作来运行代码以在完成Future
时.get
完成关联。
如果您确实Future[Future[HttpResponse]]
出于某种原因想要,考虑到someNonBlockingHttpClient
返回的速度可能非常快.get
(请记住,它是异步的,因此它可以在设置请求并计划发送后尽快返回),Future.apply
这可能不是解决之道关于事情:步骤1-5的开销可能比花费的时间更长.get
!对于这种情况,Future.successful
很有用:
val doubleWrapped: Future[Future[HttpResponse]] = Future.successful( someNonBlockingHttpClient.get("baz.com"))
Future.successful
不涉及重击,创建Promise
或安排任务ExecutionContext
(甚至不使用ExecutionContext
!)。它直接构造一个已经成功完成的Future
,但是其中包含的值Future
是Future.successful
在调用之前计算的(即执行thunk中的操作)并阻塞了调用线程。如果所讨论的代码阻塞了足够长的时间以设置某些内容以异步执行,那么这不是问题,但是它可以使长时间阻塞的内容像异步/非阻塞一样向外界展示。
知道何时使用Future.apply
和Future.successful
具有某些重要性,尤其是在您关心性能和可伸缩性时。Future.successful
比起不恰当地使用,可能更常见Future.apply
(因为它不需要隐式ExecutionContext
,我看过新手喜欢它)。就像科林·布雷克(Colin Breck)所说的那样,请勿通过不当使用来阻碍您未来的成功Future.successful
。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句