2019年7月26日 星期五

Kotlin 的 promise

若要用 Kotlin 寫 Javascript (Kotlin to JS), 使用 promise 要先 import kotlin.js.Promise,  接著宣告資料型態, 一般用法像是:
    import kotlin.js.Promise
    import kotlin.browser.*
    fun main() {   
           lateinit var resolve: (Int)->Unit
           val promise = Promise < Int > { Yes, _ ->
                resolve = Yes
           }
           promise.then { result ->    // only execute when promise fullfill
                println("I got $result")
           }
           window.setTimeout( {resolve(0)}, 2000)  // resolve result sometime or somewhere
    }
例如應用在按鍵事件驅動的不定時的異步化(async function)程式上相當實用.
promise 的原理很簡單, 只要沒呼叫  resolve( ) 去解析, 就會維持在暫停(suspend)狀態, 一旦執行過 resolve( ) 後就變成滿足(fullfill)狀態. 後續當呼叫 then { } 區塊時, 若還是在暫停狀態下,沒意外(exception)的話,就維持該狀態,但要將該區塊(指標變數)附加(累積)到 promise 物件裏面,一旦呼叫了 resolve(result), resolve 除了會解析結果外, 同時還會就將結果傳遞給之前所累積的 then { } 區塊去執行. 也就是說只有在滿足狀態當下, promise 的 then { } 區塊才會執行. 有個應用情境是:將 promise.then { } 區塊放在函式內執行, 再用像是 window.setTimeout 或是 window.window.requestAnimationFrame 定時啟動該函式, 重複啟動函式時, 若 promise 一直維持在暫停的狀態, 就會造成 then 區塊不停的累積, 一旦狀態滿足時, 就有可能造成區塊重複執行的後遺症, 例如:
     import kotlin.js.Promise
     import kotlin.browser.*
     fun main() {   
           lateinit var resolve: (Int)->Unit
           val promise = Promise < Int > { Yes, _ ->
                resolve = Yes
           }
           var count = 0
           fun render( ) {
                 val capture = count
                 promise.then { result ->  // dupcate then if not fullfill
                       println("fullfill:  result=${result} capture=${capture}")
                 }
                 if( count ++ < 10 ) window.requestAnimationFrame( {render() } )
           }
           window.requestAnimationFrame({render() })
           // promise not resolve
           window.setTimeout( {resolve(0)}, 2000)  // resolve result sometime or somewhere
     }
執行結果:
fullfill:  result=0 capture=0
fullfill:  result=0 capture=1
fullfill:  result=0 capture=2
fullfill:  result=0 capture=3
fullfill:  result=0 capture=4
fullfill:  result=0 capture=5
fullfill:  result=0 capture=6
fullfill:  result=0 capture=7
fullfill:  result=0 capture=8
fullfill:  result=0 capture=9
fullfill:  result=0 capture=10
為了用 kotlin 實現 async/await 的邏輯.  運用 promise 物件, 基本上就可以成為 async function, await 只能放在 async function 裏面, 直到 promise 完成解析(滿足狀態)才能進行後續動作, 因此特別用高階函式及 skip re-entry 技巧, 將 promise 包進新的類型, 並把他的 then 區塊打包放進高階函式當成 callback 函式去執行, 就能解決重複執行的困擾:
     import kotlin.js.Promise
     import kotlin.browser.*
     class myAsync {             
               var once = 0
               lateinit var resolve:(Int)-> Unit
               val promise = Promise < Int > { Yes, _ ->
                      resolve = Yes
               }
               fun bwait(callback: (Int) -> Unit ) { //  high order function with callback function
                    promise.then { result ->
                         if (once ++ == 0)  callback(result ) // 只執行第一次的 then  區塊
                    }
               }
               fun await(callback: (Int) -> Unit ) { //  high order function with callback function
                    once ++
                    promise.then { result ->
                         if (-- once == 0)  callback(result ) // 只執行最後一次的 then 區塊
                    }
               }
     }
     fun main() {
           val async = myAsync( )
           var count = 0
           fun render( ) {
                  val capture = count
                  async.await { result ->  // no duplicate then if fullfill
                       println("fullfill:  result=${result} capture=${capture}")
                 }
                 if( count ++ < 10 ) window.requestAnimationFrame( {render() } )
           }
           window.requestAnimationFrame( { render() } )         
           window.setTimeout( {async.resolve(0)}, 2000) // resolve result sometime or somewhere
     }
若是用 async.await  等待執行的結果:
         fullfill:  result=0   capture=10
若是用 async.bwait  等待執行的結果:
         fullfill:  result=0   capture=0


沒有留言: