編譯之后--Kotlin 協(xié)程

Kotlin 協(xié)程

本文只是淺析 Kotlin 協(xié)程在各平臺(tái)的實(shí)現(xiàn), 以及跨平臺(tái)兼容方案套利。 如果要看Kotlin協(xié)程api的用法,請(qǐng)移步我的另一篇文章

言歸正傳, 我們聊聊"協(xié)程".

如果有講的不好的地方, 歡迎在下方評(píng)論

協(xié)程介紹

首先, 什么是協(xié)程?

有人說(shuō)協(xié)程擁有自己的寄存器上下文和棧的輕量級(jí)執(zhí)行單元峡碉, 可以在控制執(zhí)行上下文的切換。

熟悉Javascript生成器的人可能會(huì)說(shuō)颂碘,協(xié)程就是回調(diào)的語(yǔ)法糖异赫,本質(zhì)?根本不關(guān)方法棧上下文切換啥事。

到底誰(shuí)說(shuō)的是對(duì)的头岔?
別著急塔拳, 看看維基百科是怎么說(shuō)的:

我們先看看下面這段描述:

Coroutines are computer-program components that generalize subroutines for non-preemptive multitasking, by allowing multiple entry points for suspending and resuming execution at certain locations

協(xié)程就是可以生成非搶占式子程序的計(jì)算機(jī)程序, 可以允許程序有多個(gè)特定的地方可以掛起或者恢復(fù)執(zhí)行峡竣。

Kotlin 協(xié)程在各平臺(tái)編譯成什么靠抑?

熟悉Javascript生成器的人可能會(huì)說(shuō), 協(xié)程就是回調(diào)的語(yǔ)法糖适掰, 本質(zhì)?根本不關(guān)方法棧上下文切換啥事颂碧。

在Kotlin 協(xié)程之前, 我們講講關(guān)于Js的故事类浪。

大家都知道Js有諸多版本载城, 我們經(jīng)常用著Es6、7费就、8的語(yǔ)法, 使用Babel將它們編譯成低版本诉瓦, 再在瀏覽器或者低版本node執(zhí)行。 這里我們單純對(duì)Node.js進(jìn)行討論力细。

低版本的Node有協(xié)程嗎睬澡? 你可以說(shuō)沒(méi)有。實(shí)現(xiàn) coroutine 的方式有很多眠蚂,比如 ES6 的 generator煞聪,ES8(你沒(méi)有看錯(cuò), 不是ES7) 的 async/await。在低版本的Node中逝慧, 自然是沒(méi)有協(xié)程的昔脯, 高級(jí)的生成器函數(shù)將會(huì)編譯成普通的Js回調(diào)、狀態(tài)機(jī)代碼(我們后面會(huì)講到)笛臣, 但是在Node.js 8之后的版本栅干, node.js原生支持了協(xié)程, 實(shí)現(xiàn)了寄存器上下文和棧的切換捐祠。

所以, 我們是不是可以說(shuō)一段高版本Js代碼如果經(jīng)過(guò)編譯成低版本代碼后桑李, 就不在擁有協(xié)程了呢踱蛀?
這么說(shuō)是不公平的, 如果這么說(shuō)的話窿给, 那本文就該換標(biāo)題了, Kotlin就沒(méi)有協(xié)程了

上層不應(yīng)該關(guān)心下層的具體實(shí)現(xiàn)率拒,而只應(yīng)該關(guān)心下層提供的接口

大家都知道崩泡, Jvm也是沒(méi)有原生協(xié)程的,大部分平臺(tái)猬膨, 包括Kotlin-js編譯后的es5 js代碼角撞, 也并沒(méi)有關(guān)于協(xié)程的原生實(shí)現(xiàn), (或者說(shuō)沒(méi)有用到)。 那么把協(xié)程看做重要特性的Kotlin勃痴, 編譯后是怎么樣的呢谒所?
Talk is cheap, show me your code.

讓我們看看下面這段(js 和 kotlin 混寫的)偽代碼

 suspend function postItems(item) {
     let tk = getToken(item)
     let post = doPost(tk)
     return post
 }

 interface StateMachine {
     status: number,
     item: Any,
     continuation: Continuation
 }

 function postI(item, sm: Continuation) {
     if (!sm instanceof StateMachine) {
         sm = {
             status: 0,
             item: null,
             continuation: sm
         }
     }

     switch sm.status:
         case 1:
             sm.status += 1;
             getToken(item, (tk) => {
                 sm.item = tk;
                 postI(null, sm)
             })
         case 2:
             sm.status += 1;
             doPost(sm.item, (post) => {
                 sm.item = post;
                 postI(null, sm)
             })
         case 3:
             sm.continuation.resume(sm.item)
             return
 }

簡(jiǎn)析:

postItems 是一個(gè)kotlin的suspend方法, 他先通過(guò)http請(qǐng)求拿到token沛申, 再通過(guò)token發(fā)起一個(gè)http post 請(qǐng)求劣领, 將返回的對(duì)象 返回給調(diào)用者。

對(duì)應(yīng)編譯后的實(shí)現(xiàn)就成了postI,一個(gè)普通的方法,接受一個(gè)Continuation類型對(duì)象的方法铁材。而這個(gè) Continuation,可以近似看做一個(gè)回調(diào)函數(shù), 也就是說(shuō), 在jvm這邊, 你的suspend function, 最終變成了接受一個(gè)回調(diào)函數(shù)為參數(shù)的普通方法, 而且在其它平臺(tái)也大抵類似尖淘。

那么suspend function里面的suspend function調(diào)用是怎么調(diào)的呢?看上面的偽代碼著觉,你大概可以知道: 通過(guò)回調(diào), 在getToken的回調(diào)里面繼續(xù)調(diào)用postItems方法村生,但是狀態(tài)已經(jīng)改變,則可以執(zhí)行到下一個(gè)suspend掛起點(diǎn)饼丘,以此類推,通過(guò)這個(gè)狀態(tài)機(jī)實(shí)現(xiàn)了原來(lái)kotlin suspend 方法的掛起與恢復(fù)趁桃。

Kotlin 跨平臺(tái)實(shí)現(xiàn)

雖然Kotlin 編譯后的普通方法提供了回調(diào)的應(yīng)用, 但是正如官方文檔中所說(shuō)葬毫, 你永遠(yuǎn)不要嘗試在各個(gè)平臺(tái)實(shí)現(xiàn)這些回調(diào)镇辉, 除非你是個(gè) "coroutine master", 實(shí)現(xiàn)context等都是有坑的。

Kotlin 協(xié)程 node.js

kotlin.coroutines 有個(gè)專門的 promise 庫(kù)贴捡, 所以如果你要暴露一個(gè)api給外面的js庫(kù)去調(diào)用忽肛, 你可以這么寫:

suspend fun helloWorld() {
  delay(1000)
  return "1212"
}

fun theApiYouWantToExposeAndUsedInOtherJsModules() {
  Globalscope.promise {
    helloworld()
  }
}

Kotlin 協(xié)程 jvm

Jvm 有runblocking 的api阻塞當(dāng)前線程執(zhí)行, 這個(gè)是一個(gè)優(yōu)點(diǎn)烂斋,因?yàn)樵趎ode.js單線程里面你沒(méi)辦法runblocking.
所以你可以暴露這樣的api:

suspend fun helloWorld() {
  delay(1000)
  return "1212"
}

fun theApiYouWantToExposeAndUsedInOtherJvmProgram() = runBlocking {
    helloworld()
  }
}

當(dāng)然為了性能更推薦的方式是利用jvm 獨(dú)有future api 返回一個(gè)Future 對(duì)象:

fun theApiYouWantToExposeAndUsedInOtherJvmProgram() = future {
    helloworld()
  }
}

Kotlin 協(xié)程 native

native 也有runblocking, 不過(guò)好像在swift 里面有個(gè)天坑屹逛。我對(duì)swift不是很了解, 解決方案給你扔這了: https://github.com/ktorio/ktor/issues/678

如果有問(wèn)題或者謬誤汛骂, 歡迎在評(píng)論指出罕模。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市帘瞭,隨后出現(xiàn)的幾起案子淑掌,更是在濱河造成了極大的恐慌,老刑警劉巖蝶念,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抛腕,死亡現(xiàn)場(chǎng)離奇詭異芋绸,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)担敌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門摔敛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人全封,你說(shuō)我怎么就攤上這事马昙。” “怎么了刹悴?”我有些...
    開封第一講書人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵行楞,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我颂跨,道長(zhǎng)敢伸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任恒削,我火速辦了婚禮池颈,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘钓丰。我一直安慰自己躯砰,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開白布携丁。 她就那樣靜靜地躺著琢歇,像睡著了一般。 火紅的嫁衣襯著肌膚如雪梦鉴。 梳的紋絲不亂的頭發(fā)上李茫,一...
    開封第一講書人閱讀 51,598評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音肥橙,去河邊找鬼魄宏。 笑死,一個(gè)胖子當(dāng)著我的面吹牛存筏,可吹牛的內(nèi)容都是我干的宠互。 我是一名探鬼主播,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼椭坚,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼予跌!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起善茎,我...
    開封第一講書人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤券册,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體汁掠,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡略吨,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了考阱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡鞠苟,死狀恐怖乞榨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情当娱,我是刑警寧澤吃既,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站跨细,受9級(jí)特大地震影響鹦倚,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜冀惭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一震叙、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧散休,春花似錦媒楼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至限府,卻和暖如春夺颤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背胁勺。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工世澜, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人姻几。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓宜狐,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親蛇捌。 傳聞我的和親對(duì)象是個(gè)殘疾皇子抚恒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容