这里将介绍不一样的异步编程实现。react
做为程序员,咱们都面临着一个问题,就是如何不让咱们的程序阻塞。不管咱们是桌面开发,移动开发,甚至服务端开发。
有不少不一样的实现来解决这个问题,包括:程序员
-Threading
-Callbacks
-Futures, Promises
-Reactive Extensions
-Coroutinesweb
咱们先简明的看下前四种实现方式。编程
到目前为止,线程是最为程序员所知的一种避免阻塞应用的实现。api
fun postItem(item: Item) { val token = preparePost() val post = submitPost(token, item) processPost(post) } fun preparePost(): Token { // makes a request and consequently blocks the main thread return token }
咱们假设 preparePost
方法是一个很耗时的方法,这样的话,它将会阻塞用户交互。咱们能够把这个方法放在一个单独的线程中,这样就避免了阻塞UI线程。这是一个很是常见的技术,可是它有一系列的缺点:promise
-线程很耗资源,它们要切换上下文
-线程数量有限,操做系统能启动的线程数量是有限的,对于服务端来讲,这是一个很大的瓶颈
-线程并不老是可用的,好比JavaScript就不支持线程
-编写好的代码并不容易,容易出现各类竞争条件,会陷入并发编程苦海数据结构
简单来讲,就是把函数做为参数传递给另外一个函数,一旦自己函数执行完成,就执行传参的函数。并发
fun postItem(item: Item) { preparePostAsync { token -> submitPostAsync(token, item) { post -> processPost(post) } } } fun preparePostAsync(callback: (Token) -> Unit) { // make request and return immediately // arrange callback to be invoked later }
这个看起来相对优雅的解决了问题,可是一样有几个问题:异步
-对于多重嵌套回调,这将是一个很是难理解的方式
-错误处理变得很复杂,对于多重嵌套,错误的传递和处理变得很复杂async
Callbacks在事件循环结构的语言中比较常见,好比JavaScript,但更多的码农倾向于其它的解决方案,好比promises或者reactive extensions.
Futures,Promises,不一样的平台或语言有不一样的叫法,它是承若将在某个点返回一个叫Promise的对象,简略操做以下所示:
fun postItem(item: Item) { preparePostAsync() .thenCompose { token -> submitPostAsync(token, item) } .thenAccept { post -> processPost(post) } } fun preparePostAsync(): Promise<Token> { // makes request an returns a promise that is completed later return promise }
这个方式对于咱们来讲,有一点挑战,好比说:
-不一样的编程模型。和callback相似,从上而下的链式回调。传统的编程结构,好比循环,错误处理都不在有效
-不一样的平台有不一样的API
-具体的返回类型,返回类型并非咱们实际的数据结构
-错误处理变得复杂。错误的传递处理并不清晰明确。
响应式扩展(Rx)被Eik Meijer引入C#,虽然它确实存在于.NET平台,但直到被Netflix移植到Java平台,才逐渐开始成为主流。从这起,各个平台实现本身的响应式扩展,包括JavaScript(RxJS)。
Rx的背后思想是把数据看成流来处理,而且该流能够被观测。实际上,Rx只是观察者模式,带有一系列的扩展来操做数据。
Rx和Futures很相似,但不一样的是,咱们能够认为Future返回一个直接的元素,而Rx返回流。另外一方面,它提供了一中新的编程模型,就像所宣传的口号那样:
everything is a stream, and it’s observable
这意味着有不一样的解决方法,而且提供了不一样的思路去写异步代码。相比于Futures不一样的是,Rx被移植到多个平台,咱们有着一致的API体验。包括C#,Java,JavaScript,或者其它实现了Rx的语言。
此外,Rx引入了更友好的错误处理方式。
Kotlin异步编程的方式是使用coroutines,这是一种可挂起的执行方式,它能够在某一点挂起,以后从这点恢复继续执行。
对于码农来讲,使用协程方法编写异步代码和编写同步代码没有什么不一样,这中编码模型并不存在什么挑战。
举个栗子:
fun postItem(item: Item) { launch { val token = preparePost() val post = submitPost(token, item) processPost(post) } } suspend fun preparePost(): Token { // makes a request and suspends the coroutine return suspendCoroutine { /* ... */ } }
这段代码将会执行耗时工做,但并不阻塞主线程。带有 suspend
修饰的 preparePost
方法就是被成为可挂起函数。就像上面描述的那样,它能够在某个点挂起,而后从该点恢复执行。
-函数签名保持了一致,仅增长了 suspend
修饰符。返回值也是咱们想要的类型
-编写代码和咱们之前同样,并不须要特殊的语法
-编程模型和API保持了同样,咱们能够继续使用循环,异常处理,不须要学习新的api集
-它是平台无关的,不管是运行在JVM,JavaScript平台,仍是其它,咱们写法都是同样的,编译器负责适配各个平台。
Coroutines并非Kotlin发明的新概念,它们已经存在了几十年了,而且在其它语言很是流行,好比Go语言。须要注意的是,虽然Kotlin实现了Coroutines,但大多数功能都是在库函数里边。事实上,除了 suspend
关键词,没有其它的关键词被引入到语言中。这个和C#有着较大的不一样,C#引入 async
和 await
作为语法的一部分。而对于Kotlin,这些只是库函数。