こんにちは, Web事業部の西村です
私の前回の記事 では dyanmic
の利用をなくすようにしました
そして今回は, Callbackの呼び出しをやめて, JavaScriptの非同期処理( Promise
)になるよう変更したいと思います
また, Promise
は スタンダード
なものと Coroutines
のものの2種類がありますので, この記事ではその両方を紹介したいと思います
目次
過去の記事
なぜ非同期に?
非同期処理することにより複数の処理を同時に実行することができます
例えば, 複数のファイルを受け取り, S3に保存する
といったことを考えてみます
非同期処理ではない場合, 1ずつしかファイルのアップロードができません
非同期処理の場合, 複数のファイルを同時にアップロードできるようになるので時間の短縮が図れます
注意事項
この記事では Promise
の詳しい解説は行いません
詳しく知りたい方は下記の記事を参考にしてください
qiita.com
開発環境
この記事では下記の環境で開発を行っています
- AdoptOpenJDK 1.8.0_222-b10
- IntelliJ IDEA Community 2019.2.2
- Kotlin 1.3.50
プロジェクト
前回の記事 まで作成したプロジェクトを利用します
プロジェクトの構成は下記のようになっています
KotlinLambda ├─.gradle/ ├─.idea/ ├─build/ ├─gradle/ ├─src/ │ └─main/ │ └─kotlin/ │ └─jp.co.seeds_std.lambda.kotlin/ │ └─handler.kt ├─build.gradle.kts ├─compress.zip ├─gradlew ├─gradlew.bat └─settings.gradle.kts
また, handler.ktは下記のようになっています
package jp.co.seeds_std.lambda.kotlin import kotlin.js.Json import kotlin.js.Promise import kotlin.js.json @JsExport @JsName("handler") fun handler(event: Json, context: Json, callback: (Error?, Response?) -> Unit) { val response = Response(body = "Hello from Kotlin Class Lambda!") callback(null, response) } data class Response( val statusCode: Int = 200, val body: String = "{}", val isBase64Encoded: Boolean = false, val headers: Json = json(), val multiValueHeaders: Json = json() )
スタンダードなPromise
まずはkotlin-stdlibに実装されている Promise
を利用してみます
関数の書き換え
handler
関数を変更します
handler.kt
@JsExport @JsName("handler") fun handler(event: Json, context: Json, callback: (Error?, Response?) -> Unit) { val response = Response(body = "Hello from Kotlin Class Lambda!") callback(null, response) }
↓
@JsExport @JsName("handler") fun handler(event: Json, context: Json) = Promise<Response> { executor, reject -> executor(Response(body = "Hello from Kotlin Async Lambda!")) }
executor
は処理成功時に実行する関数で, 引数はジェネリクスで指定したクラス (この記事ではResponse
) となりますreject
は処理失敗時に実行する関数で, 引数はThrowable
クラスのオブジェクトとなります
動作確認
関数をデプロイし, API GatewayのURLにアクセスし Hello from Kotlin Async Lambda!
と表示されれば成功です
もう少し恩恵を受けてみる(スタンダード)
今度はNode.jsの setTimeout
を利用して本当に非同期に処理されているかを確認したいと思います
その前に, Kotlin/JSにはNode.jsの setTimeout
は定義されていないので追加します
今回追加する場所はコードの最下部に追加します
handler.kt
external fun setTimeout(callback: dynamic, delay: Int, vararg args: Any?): Int external fun clearTimeout(timeout: Int)
external
: 外部の関数/変数を利用するために記述します。トランスパイルした場合にそのまま残るようになりますcallback
の部分がdynamic
となっていますが, 関数の引数が固定値ではないためやむを得ずdynamic
としています
続いて handler
関数を変更します
handler.kt
@JsExport @JsName("handler") fun handler(event: Json, context: Json) = Promise<Response> { executor, reject -> val results = (1..10).map { Promise<Int> { childExecutor, _ -> setTimeout({childExecutor(it)}, 1000) } } Promise.all(results.toTypedArray()).then { executor(Response(body = "Result: ${it.sum()} - Standard")) } }
このコードでは 1秒待ってから数字を返す
というコードを10回繰り返すものとなっています
動作確認
関数をデプロイし, API GatewayのURLにアクセスし約1秒後に Result: 55 - Standard
と表示されれば成功です
※初回の実行では2-3秒ほどかかる場合もあります
通常の処理であれば 1秒待って数字を返す
を10回行えば10秒かかるはずですが, 非同期に実行されているため約1秒で完了します
CoroutinesのPromise
標準実装の Promise
では見にくく感じませんでしたか?(私は少なからず見にくいなと感じました)
非同期処理を書くのであれば, Node.jsみたいに async
で書きたいところです
そこで使うのが kotlinx.coroutines
です
依存関係を追加する
kotlinx.coroutines
は外部ライブラリなので build.gradle.kts
に依存関係を追加する必要があります
build.gradle.kts
implementation(kotlin("stdlib-js")) implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-js:1.3.2") // 追加
コードを書き換える
まずはただの文字列を返すように handelr
関数を変更します
handler.kt
@JsExport @JsName("handler") fun handler(event: Json, context: Json) = GlobalScope.promise { return@promise Response(body = "Hello from Kotlin Coroutines Lambda!") }
先ほどのコードと比較するとかなり単調なものになりました
※エラーを返す際は throw NullPointerException()
のように throw
するだけとなります
動作確認
関数をデプロイし, API GatewayのURLにアクセスし Hello from Kotlin Coroutines Lambda!
と表示されれば成功です
もう少し恩恵を受けてみる(Coroutines)
先ほどと同じ関数を実装してみます
handler.kt
@JsExport @JsName("handler") fun handler(event: Json, context: Json) = GlobalScope.promise { val tasks = (1..10).map { async { delay(1000) it } } val results = tasks.awaitAll() return@promise Response(body = "Result: ${results.sum()} - Coroutines") }
先ほどとは異なり, CoroutineScope
内で実行できるため, async
関数を利用できます
動作確認
関数をデプロイし, API GatewayのURLにアクセスし約1秒後に Result: 55 - Coroutines
と表示されれば成功です
※初回の実行では2-3秒ほどかかる場合もあります
最後に
いかがでしたでしょうか
非同期処理が行えるとよりできることの幅が増えるかと思います
また、Coroutinesの Promise
と標準実装の Promise
は互換性があるため外部ライブラリが非同期処理を行う場合にも使え,
よりKotlinらしい書き方ができるようになっていくと思います
次回は kotlinx.serialization
を用いてJsonのレスポンスを行う記事を書きたいと思います
ここまで読んでいただきありございました