首頁 > 軟體

Kotlin Suspend掛起函數的使用詳解

2023-02-20 06:01:15

總結

掛起(suspend)函數是所有協程的核心。 掛起函數可以執行長時間執行的操作並等待它完成而不會阻塞主執行緒。

掛起函數的語法與常規函數的語法類似,不同之處在於新增了suspend關鍵字。 它可以接受一個引數並有一個返回型別。 但是,掛起函數只能由另一個掛起函數或在協程內呼叫。

suspend fun backgroundTask(param: Int): Int {
     // long running operation
}

在背後,編譯器將掛起函數轉換為另一個沒有掛起關鍵字的函數,該函數接受一個型別為 Continuation<T> 的附加引數。 例如,上面的函數將由編譯器轉換為:

fun backgroundTask(param: Int, callback: Continuation<Int>): Int {
   // long running operation
}

本質

  • 掛起函數只能在協程或者其他掛起函數中呼叫。
  • 掛起的物件是協程:launch ,async 或者其他函數建立的協程,在執行到某一個 suspend 函數的時候,這個協程會被掛起,即,從正在執行它的執行緒上脫離。就是說,當前執行緒跳過這個掛起函數,繼續往下執行,但另一方面,執行緒的程式碼在到達 suspend 函數的時候被掐斷,接下來協程會從這個 suspend 函數開始繼續往下執行,不過是在指定的執行緒,執行完後,返回到之前掛起它的執行緒;
  • 簡單來講,在 Kotlin 中所謂的掛起,就是一個稍後會被自動切回來的執行緒排程操作;
  • 掛起函數的特點是使用同步的方式完成非同步任務。
  • withContext 的作用就是指定切換的執行緒,比如:suspend fun suspendingGetImage(id: String) = withContext(Dispatchers.IO)

何時使用

如果你的某個函數比較耗時,也就是要等的操作,那就把它寫成 suspend 函數。這就是原則。

耗時操作一般分為兩類:I/O 操作和 CPU 計算工作。比如檔案的讀寫、網路互動、圖片的模糊處理,都是耗時的,通通可以把它們寫進 suspend 函數裡。

另外這個「耗時」還有一種特殊情況,就是這件事本身做起來並不慢,但它需要等待,比如 5 秒鐘之後再做這個操作。這種也是 suspend 函數的應用場景。

消除回撥

假設 postItem 由三個有依賴關係的非同步子任務組成: requestTokencreatePostprocessPost,這三個函數都是基於回撥的 API:

// 三個基於回撥的 API
fun requestToken(block: (String) -> Unit)
fun createPost(
  token: String,
  item: Item,
  block: (Post) -> Unit)
)
fun processPost(post: Post)
fun postItem(item: Item) {
  requestToken { token ->
    createPost(token, item) { post ->
      processPost(post)
    }
  }
}

可以看到基於回撥的 API 很容易造成大量縮排。如果程式碼中再加上一些條件、迴圈的邏輯,那麼程式碼可讀性會大大降低。Kotlin 的 suspend 關鍵字可以幫助我們消除回撥,用同步的寫法寫非同步:

suspend fun requestToken(): String
suspend fun createPost(token: String, item: Item): Post
suspend fun processPost(post)
suspend fun postItem(item: Item) {
  val token = 

IT145.com E-mail:sddin#qq.com