异步编程技术

异步编程技术

这里将介绍不一样的异步编程实现。react

做为程序员,咱们都面临着一个问题,就是如何不让咱们的程序阻塞。不管咱们是桌面开发,移动开发,甚至服务端开发。
有不少不一样的实现来解决这个问题,包括:程序员

-Threading
-Callbacks
-Futures, Promises
-Reactive Extensions
-Coroutinesweb

咱们先简明的看下前四种实现方式。编程

Threading

到目前为止,线程是最为程序员所知的一种避免阻塞应用的实现。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就不支持线程
-编写好的代码并不容易,容易出现各类竞争条件,会陷入并发编程苦海数据结构

Callbacks

简单来讲,就是把函数做为参数传递给另外一个函数,一旦自己函数执行完成,就执行传参的函数。并发

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

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
-具体的返回类型,返回类型并非咱们实际的数据结构
-错误处理变得复杂。错误的传递处理并不清晰明确。

Reactive Extensions

响应式扩展(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引入了更友好的错误处理方式。

Coroutines

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#引入 asyncawait 作为语法的一部分。而对于Kotlin,这些只是库函数。