Upload
others
View
36
Download
0
Embed Size (px)
Citation preview
FreshAsyncWithKotlin
PresentedatQCon SF,2017/RomanElizarov@JetBrains
Speaker:RomanElizarov
• 16+yearsexperience• Previouslydevelopedhigh-perftradingsoftware@Devexperts• Teachconcurrent&[email protected]• Chiefjudge@NorthernEurasiaContest/ACMICPC• NowteamleadinKotlinLibraries@JetBrains
Pragmatic.Concise.Modern.InteroperablewithJava.
AsynchronousProgramming
Howdowewritecodethatwaitsforsomethingmostofthetime?
AtoyproblemKotlin fun requestToken(): Token {
// makes request for a token & waitsreturn token // returns result when received
}
1
fun requestToken(): Token { … }fun createPost(token: Token, item: Item): Post {
// sends item to the server & waits return post // returns resulting post
}
AtoyproblemKotlin
2
fun requestToken(): Token { … }fun createPost(token: Token, item: Item): Post { … }fun processPost(post: Post) {
// does some local processing of result}
AtoyproblemKotlin
3
fun requestToken(): Token { … }fun createPost(token: Token, item: Item): Post { … }fun processPost(post: Post) { … }
AtoyproblemKotlin
fun postItem(item: Item) {val token = requestToken()val post = createPost(token, item)processPost(post)
}
123
Canbedonewiththreads!
fun requestToken(): Token {// makes request for a token // blocks the thread waiting for resultreturn token // returns result when received
}fun createPost(token: Token, item: Item): Post { … }fun processPost(post: Post) { … }
Threads
fun postItem(item: Item) {val token = requestToken()val post = createPost(token, item)processPost(post)
}
Isanythingwrongwithit?
Howmanythreadswecanhave?
100🙂
Howmanythreadswecanhave?
1000😅
Howmanythreadswecanhave?
10000😩
Howmanythreadswecanhave?
100000😵
CallbackstotherescueSortof…
Callbacks:beforefun requestToken(): Token {
// makes request for a token & waitsreturn token // returns result when received
}
1
Callbacks:afterfun requestTokenAsync(cb: (Token) -> Unit) {
// makes request for a token, invokes callback when done// returns immediately
}
1callback
Callbacks:beforefun requestTokenAsync(cb: (Token) -> Unit) { … }fun createPost(token: Token, item: Item): Post {
// sends item to the server & waits return post // returns resulting post
}
2
Callbacks:afterfun requestTokenAsync(cb: (Token) -> Unit) { … }fun createPostAsync(token: Token, item: Item,
cb: (Post) -> Unit) {// sends item to the server, invokes callback when done// returns immediately
}
2callback
Callbacks:beforefun requestTokenAsync(cb: (Token) -> Unit) { … }fun createPostAsync(token: Token, item: Item,
cb: (Post) -> Unit) { … }fun processPost(post: Post) { … }
fun postItem(item: Item) {val token = requestToken()val post = createPost(token, item)processPost(post)
}
Callbacks:afterfun requestTokenAsync(cb: (Token) -> Unit) { … }fun createPostAsync(token: Token, item: Item,
cb: (Post) -> Unit) { … }fun processPost(post: Post) { … }
fun postItem(item: Item) {requestTokenAsync { token ->
createPostAsync(token, item) { post ->processPost(post)
}}
} aka“callbackhell”
Thisissimplified.Handlingexceptionsmakesitarealmess
Futures/Promises/RxtotherescueSortof…
Futures:beforefun requestTokenAsync(cb: (Token) -> Unit) {
// makes request for a token, invokes callback when done// returns immediately
}
1
Futures:afterfun requestTokenAsync(): Promise<Token> {
// makes request for a token// returns promise for a future result immediately
}
1future
Futures:beforefun requestTokenAsync(): Promise<Token> { … }fun createPostAsync(token: Token, item: Item,
cb: (Post) -> Unit) {// sends item to the server, invokes callback when done// returns immediately
}
2
Futures:afterfun requestTokenAsync(): Promise<Token> { … }fun createPostAsync(token: Token, item: Item): Promise<Post> {
// sends item to the server // returns promise for a future result immediately
}
future2
Futures:beforefun requestTokenAsync(): Promise<Token> { … }fun createPostAsync(token: Token, item: Item): Promise<Post> …fun processPost(post: Post) { … }
fun postItem(item: Item) {requestTokenAsync { token ->
createPostAsync(token, item) { post ->processPost(post)
}}
}
Futures:afterfun requestTokenAsync(): Promise<Token> { … }fun createPostAsync(token: Token, item: Item): Promise<Post> …fun processPost(post: Post) { … }
fun postItem(item: Item) {requestTokenAsync()
.thenCompose { token -> createPostAsync(token, item) }
.thenAccept { post -> processPost(post) }}
Composable &propagatesexceptions
Nonestingindentation
Futures:afterfun requestTokenAsync(): Promise<Token> { … }fun createPostAsync(token: Token, item: Item): Promise<Post> …fun processPost(post: Post) { … }
fun postItem(item: Item) {requestTokenAsync()
.thenCompose { token -> createPostAsync(token, item) }
.thenAccept { post -> processPost(post) }}
Butallthosecombinators…
KotlincoroutinestotherescueLet’sgetreal
Coroutines:beforefun requestTokenAsync(): Promise<Token> {
// makes request for a token// returns promise for a future result immediately
}
1
Coroutines:aftersuspend fun requestToken(): Token {
// makes request for a token & suspendsreturn token // returns result when received
}
1naturalsignature
Coroutines:beforesuspend fun requestToken(): Token { … }fun createPostAsync(token: Token, item: Item): Promise<Post> {
// sends item to the server // returns promise for a future result immediately
}
2
Coroutines:aftersuspend fun requestToken(): Token { … }suspend fun createPost(token: Token, item: Item): Post {
// sends item to the server & suspendsreturn post // returns result when received
}
2naturalsignature
Coroutines:beforesuspend fun requestToken(): Token { … }suspend fun createPost(token: Token, item: Item): Post { … }fun processPost(post: Post) { … }
fun postItem(item: Item) {requestTokenAsync()
.thenCompose { token -> createPostAsync(token, item) }
.thenAccept { post -> processPost(post) }}
Coroutines:aftersuspend fun requestToken(): Token { … }suspend fun createPost(token: Token, item: Item): Post { … }fun processPost(post: Post) { … }
suspend fun postItem(item: Item) {val token = requestToken()val post = createPost(token, item)processPost(post)
}
Coroutines:aftersuspend fun requestToken(): Token { … }suspend fun createPost(token: Token, item: Item): Post { … }fun processPost(post: Post) { … }
suspend fun postItem(item: Item) {val token = requestToken()val post = createPost(token, item)processPost(post)
}
Likeregular code
Coroutines:aftersuspend fun requestToken(): Token { … }suspend fun createPost(token: Token, item: Item): Post { … }fun processPost(post: Post) { … }
suspend fun postItem(item: Item) {val token = requestToken()val post = createPost(token, item)processPost(post)
}
suspensionpoints
• Regularloops
Bonusfeatures
for ((token, item) in list) {createPost(token, item)
}
• Regularexceptionhanding
Bonusfeatures
try { createPost(token, item)
} catch (e: BadTokenException) { …
}
• Regularhigher-orderfunctions
• forEach,let,apply,repeat,filter,map,use,etc
Bonusfeatures
file.readLines().forEach { line ->createPost(token, line.toItem())
}
• Customhigher-orderfunctions
Bonusfeatures
val post = retryIO {createPost(token, item)
}
Everythinglikeinblockingcode
Howdoesitwork?Aquickpeekbehindthescenes
Kotlinsuspendingfunctions
callback
Kotlin
Java/JVM
suspend fun createPost(token: Token, item: Item): Post { … }
Object createPost(Token token, Item item, Continuation<Post> cont) { … }
Kotlinsuspendingfunctions
callback
Kotlin
Java/JVM
Continuationisagenericcallbackinterface
suspend fun createPost(token: Token, item: Item): Post { … }
Object createPost(Token token, Item item, Continuation<Post> cont) { … }
interface Continuation<in T> {val context: CoroutineContextfun resume(value: T)fun resumeWithException(exception: Throwable)
}
Kotlinsuspendingfunctions
callback
Kotlin
Java/JVM
suspend fun createPost(token: Token, item: Item): Post { … }
Object createPost(Token token, Item item, Continuation<Post> cont) { … }
interface Continuation<in T> {val context: CoroutineContextfun resume(value: T)fun resumeWithException(exception: Throwable)
}
Kotlinsuspendingfunctions
callback
Kotlin
Java/JVM
suspend fun createPost(token: Token, item: Item): Post { … }
Object createPost(Token token, Item item, Continuation<Post> cont) { … }
interface Continuation<in T> {val context: CoroutineContextfun resume(value: T)fun resumeWithException(exception: Throwable)
}
Kotlinsuspendingfunctions
callback
Kotlin
Java/JVM
suspend fun createPost(token: Token, item: Item): Post { … }
Object createPost(Token token, Item item, Continuation<Post> cont) { … }
interface Continuation<in T> {val context: CoroutineContextfun resume(value: T)fun resumeWithException(exception: Throwable)
}
CodewithsuspensionpointsKotlin
Java/JVMCompilestostatemachine(simplifiedcodeshown)
val token = requestToken()val post = createPost(token, item)processPost(post)
switch (cont.label) {case 0:
cont.label = 1;requestToken(cont);break;
case 1:Token token = (Token) prevResult;cont.label = 2;createPost(token, item, cont);break;
case 2:Post post = (Post) prevResult;processPost(post);break;
}
CodewithsuspensionpointsKotlin
Java/JVM
val token = requestToken()val post = createPost(token, item)processPost(post)
switch (cont.label) {case 0:
cont.label = 1;requestToken(cont);break;
case 1:Token token = (Token) prevResult;cont.label = 2;createPost(token, item, cont);break;
case 2:Post post = (Post) prevResult;processPost(post);break;
}
IntegrationZoooffuturesonJVM
interface Service {fun createPost(token: Token, item: Item): Call<Post>
}
Retrofitasync
interface Service {fun createPost(token: Token, item: Item): Call<Post>
}
suspend fun createPost(token: Token, item: Item): Post =serviceInstance.createPost(token, item).await()
naturalsignature
interface Service {fun createPost(token: Token, item: Item): Call<Post>
}
suspend fun createPost(token: Token, item: Item): Post =serviceInstance.createPost(token, item).await()
Suspendingextensionfunctionfromintegrationlibrary
suspend fun <T> Call<T>.await(): T {…
}
Callbackseverywhere
suspend fun <T> Call<T>.await(): T {enqueue(object : Callback<T> {
override fun onResponse(call: Call<T>, response: Response<T>) {// todo
}
override fun onFailure(call: Call<T>, t: Throwable) {// todo
}})
}
suspend fun <T> Call<T>.await(): T = suspendCoroutine { cont ->enqueue(object : Callback<T> {
override fun onResponse(call: Call<T>, response: Response<T>) {if (response.isSuccessful)
cont.resume(response.body()!!)else
cont.resumeWithException(ErrorResponse(response))}
override fun onFailure(call: Call<T>, t: Throwable) {cont.resumeWithException(t)
}})
}
suspend fun <T> suspendCoroutine(block: (Continuation<T>) -> Unit): T
suspend fun <T> suspendCoroutine(block: (Continuation<T>) -> Unit): T
suspend fun <T> suspendCoroutine(block: (Continuation<T>) -> Unit): T
Regularfunction
Inspiredbycall/cc fromScheme
suspend fun <T> Call<T>.await(): T = suspendCoroutine { cont ->enqueue(object : Callback<T> {
override fun onResponse(call: Call<T>, response: Response<T>) {if (response.isSuccessful)
cont.resume(response.body()!!)else
cont.resumeWithException(ErrorResponse(response))}
override fun onFailure(call: Call<T>, t: Throwable) {cont.resumeWithException(t)
}})
}
Installcallback
suspend fun <T> Call<T>.await(): T = suspendCoroutine { cont ->enqueue(object : Callback<T> {
override fun onResponse(call: Call<T>, response: Response<T>) {if (response.isSuccessful)
cont.resume(response.body()!!)else
cont.resumeWithException(ErrorResponse(response))}
override fun onFailure(call: Call<T>, t: Throwable) {cont.resumeWithException(t)
}})
}
Installcallback
suspend fun <T> Call<T>.await(): T = suspendCoroutine { cont ->enqueue(object : Callback<T> {
override fun onResponse(call: Call<T>, response: Response<T>) {if (response.isSuccessful)
cont.resume(response.body()!!)else
cont.resumeWithException(ErrorResponse(response))}
override fun onFailure(call: Call<T>, t: Throwable) {cont.resumeWithException(t)
}})
}
Analyzeresponse
suspend fun <T> Call<T>.await(): T = suspendCoroutine { cont ->enqueue(object : Callback<T> {
override fun onResponse(call: Call<T>, response: Response<T>) {if (response.isSuccessful)
cont.resume(response.body()!!)else
cont.resumeWithException(ErrorResponse(response))}
override fun onFailure(call: Call<T>, t: Throwable) {cont.resumeWithException(t)
}})
}
Analyzeresponse
suspend fun <T> Call<T>.await(): T = suspendCoroutine { cont ->enqueue(object : Callback<T> {
override fun onResponse(call: Call<T>, response: Response<T>) {if (response.isSuccessful)
cont.resume(response.body()!!)else
cont.resumeWithException(ErrorResponse(response))}
override fun onFailure(call: Call<T>, t: Throwable) {cont.resumeWithException(t)
}})
}That’sall
Out-of-theboxintegrations
kotlinx-coroutines-core
jdk8
guava
nio
reactor
rx1
rx2
CoroutinebuildersHowcanwestartacoroutine?
Coroutinesrevisitedsuspend fun requestToken(): Token { … }suspend fun createPost(token: Token, item: Item): Post { … }fun processPost(post: Post) { … }
suspend fun postItem(item: Item) {val token = requestToken()val post = createPost(token, item)processPost(post)
}
Coroutinesrevisitedsuspend fun requestToken(): Token { … }suspend fun createPost(token: Token, item: Item): Post { … }fun processPost(post: Post) { … }
fun postItem(item: Item) {val token = requestToken()val post = createPost(token, item)processPost(post)
}
Coroutinesrevisitedsuspend fun requestToken(): Token { … }suspend fun createPost(token: Token, item: Item): Post { … }fun processPost(post: Post) { … }
fun postItem(item: Item) {val token = requestToken()val post = createPost(token, item)processPost(post)
}
Error: Suspendfunction'requestToken'shouldbecalledonlyfromacoroutineoranothersuspendfunction
Coroutinesrevisitedsuspend fun requestToken(): Token { … }suspend fun createPost(token: Token, item: Item): Post { … }fun processPost(post: Post) { … }
fun postItem(item: Item) {val token = requestToken()val post = createPost(token, item)processPost(post)
}
Cansuspend execution
Coroutinesrevisitedsuspend fun requestToken(): Token { … }suspend fun createPost(token: Token, item: Item): Post { … }fun processPost(post: Post) { … }
fun postItem(item: Item) {val token = requestToken()val post = createPost(token, item)processPost(post)
}
Cansuspend executionAregularfunctioncannot
Coroutinesrevisitedsuspend fun requestToken(): Token { … }suspend fun createPost(token: Token, item: Item): Post { … }fun processPost(post: Post) { … }
fun postItem(item: Item) {val token = requestToken()val post = createPost(token, item)processPost(post)
}
Cansuspend executionAregularfunctioncannot
Onecannotsimplyinvokeasuspendingfunction
Launch
fun postItem(item: Item) {launch {
val token = requestToken()val post = createPost(token, item)processPost(post)
}}
coroutinebuilder
fun postItem(item: Item) {launch {
val token = requestToken()val post = createPost(token, item)processPost(post)
}}
Fireandforget!
Returnsimmediately,coroutineworksinbackgroundthreadpool
fun postItem(item: Item) {launch {
val token = requestToken()val post = createPost(token, item)processPost(post)
}}
fun postItem(item: Item) {launch(UI) {
val token = requestToken()val post = createPost(token, item)processPost(post)
}}
UIContext
Justspecifythecontext
fun postItem(item: Item) {launch(UI) {
val token = requestToken()val post = createPost(token, item)processPost(post)
}}
UIContext
AnditgetsexecutedonUIthread
Where’sthemagicoflaunch?
fun launch(context: CoroutineContext = DefaultDispatcher,block: suspend () -> Unit
): Job { … }
Aregularfunction
fun launch(context: CoroutineContext = DefaultDispatcher,block: suspend () -> Unit
): Job { … } suspendinglambda
fun launch(context: CoroutineContext = DefaultDispatcher,block: suspend () -> Unit
): Job { … }
async /awaitTheclassicapproach
Kotlin-way
suspend fun postItem(item: Item) {val token = requestToken()val post = createPost(token, item)processPost(post)
}
suspend fun requestToken(): Token { … }suspend fun createPost(token: Token, item: Item): Post { … }fun processPost(post: Post) { … }
Kotlin
async Task postItem(Item item) { var token = await requestToken(); var post = await createPost(token, item); processPost(post);
}
Classic-way
C#approachtothesameproblem(alsoPython,TS,Dart,comingtoJS)
async Task<Token> requestToken() { … }async Task<Post> createPost(Token token, Item item) { … }void processPost(Post post) { … }
C#
async Task postItem(Item item) { var token = await requestToken(); var post = await createPost(token, item); processPost(post);
}
Classic-way
markwithasync
async Task<Token> requestToken() { … }async Task<Post> createPost(Token token, Item item) { … }void processPost(Post post) { … }
C#
async Task postItem(Item item) { var token = await requestToken(); var post = await createPost(token, item); processPost(post);
}
Classic-way
useawaittosuspend
async Task<Token> requestToken() { … }async Task<Post> createPost(Token token, Item item) { … }void processPost(Post post) { … }
C#
async Task postItem(Item item) { var token = await requestToken(); var post = await createPost(token, item); processPost(post);
}
Classic-way
returnsafuture
async Task<Token> requestToken() { … }async Task<Post> createPost(Token token, Item item) { … }void processPost(Post post) { … }
C#
Whynoawait keywordinKotlin?
Theproblemwithasync
requestToken() VALID –>producesTask<Token>
await requestToken() VALID –>producesToken
concurrentbehavior
sequentialbehavior
C#
C#
default
ConcurrencyishardConcurrencyhastobeexplicit
Kotlinsuspendingfunctionsaredesignedtoimitatesequential behavior
bydefault
ConcurrencyishardConcurrencyhastobeexplicit
KotlinapproachtoasyncConcurrencywhereyouneedit
Use-caseforasync
async Task<Image> loadImageAsync(String name) { … } C#
Use-caseforasync
var promise1 = loadImageAsync(name1);var promise2 = loadImageAsync(name2);
async Task<Image> loadImageAsync(String name) { … }
Startmultipleoperationsconcurrently
C#
Use-caseforasync
var promise1 = loadImageAsync(name1);var promise2 = loadImageAsync(name2);
var image1 = await promise1;var image2 = await promise2;
async Task<Image> loadImageAsync(String name) { … }
andthenwaitforthem
C#
Use-caseforasync
var result = combineImages(image1, image2);
var promise1 = loadImageAsync(name1);var promise2 = loadImageAsync(name2);
var image1 = await promise1;var image2 = await promise2;
async Task<Image> loadImageAsync(String name) { … } C#
Kotlinasync function
fun loadImageAsync(name: String): Deferred<Image> = async { … }
Kotlin
Kotlinasync function
fun loadImageAsync(name: String): Deferred<Image> = async { … }
Aregularfunction
Kotlin
Kotlinasync function
fun loadImageAsync(name: String): Deferred<Image> = async { … }
Kotlin’s futuretype
Kotlin
Kotlinasync function
fun loadImageAsync(name: String): Deferred<Image> = async { … }
async coroutinebuilder
Kotlin
Kotlinasync function
fun loadImageAsync(name: String): Deferred<Image> = async { … }
val deferred1 = loadImageAsync(name1)val deferred2 = loadImageAsync(name2)
Startmultipleoperationsconcurrently
Kotlin
Kotlinasync function
fun loadImageAsync(name: String): Deferred<Image> = async { … }
val deferred1 = loadImageAsync(name1)val deferred2 = loadImageAsync(name2)
val image1 = deferred1.await()val image2 = deferred2.await() andthenwaitforthem
awaitfunction
Suspendsuntildeferrediscomplete
Kotlin
Kotlinasync function
fun loadImageAsync(name: String): Deferred<Image> = async { … }
val deferred1 = loadImageAsync(name1)val deferred2 = loadImageAsync(name2)
val image1 = deferred1.await()val image2 = deferred2.await()
val result = combineImages(image1, image2)
Kotlin
Usingasync functionwhenneeded
suspend fun loadImage(name: String): Image { … }
Isdefinedassuspendingfunction,notasync
Usingasync functionwhenneeded
suspend fun loadImage(name: String): Image { … }
suspend fun loadAndCombine(name1: String, name2: String): Image { val deferred1 = async { loadImage(name1) }val deferred2 = async { loadImage(name2) }return combineImages(deferred1.await(), deferred2.await())
}
Usingasync functionwhenneeded
suspend fun loadImage(name: String): Image { … }
suspend fun loadAndCombine(name1: String, name2: String): Image { val deferred1 = async { loadImage(name1) }val deferred2 = async { loadImage(name2) }return combineImages(deferred1.await(), deferred2.await())
}
Usingasync functionwhenneeded
suspend fun loadImage(name: String): Image { … }
suspend fun loadAndCombine(name1: String, name2: String): Image { val deferred1 = async { loadImage(name1) }val deferred2 = async { loadImage(name2) }return combineImages(deferred1.await(), deferred2.await())
}
Usingasync functionwhenneeded
suspend fun loadImage(name: String): Image { … }
suspend fun loadAndCombine(name1: String, name2: String): Image { val deferred1 = async { loadImage(name1) }val deferred2 = async { loadImage(name2) }return combineImages(deferred1.await(), deferred2.await())
}
Kotlin approachtoasync
requestToken() VALID –>producesToken
async { requestToken() } VALID –>producesDeferred<Token>
sequentialbehavior
concurrentbehavior
Kotlin
Kotlin
default
Whatarecoroutinesconceptually?
Whatarecoroutinesconceptually?Coroutinesarelikevery light-weightthreads
fun main(args: Array<String>) = runBlocking<Unit> {val jobs = List(100_000) {
launch {delay(1000L)print(".")
}}jobs.forEach { it.join() }
}
Example
fun main(args: Array<String>) = runBlocking<Unit> {val jobs = List(100_000) {
launch {delay(1000L)print(".")
}}jobs.forEach { it.join() }
}
Example Thiscoroutinebuilderrunscoroutineinthecontextofinvokerthread
fun main(args: Array<String>) = runBlocking<Unit> {val jobs = List(100_000) {
launch {delay(1000L)print(".")
}}jobs.forEach { it.join() }
}
Example
fun main(args: Array<String>) = runBlocking<Unit> {val jobs = List(100_000) {
launch {delay(1000L)print(".")
}}jobs.forEach { it.join() }
}
Example
fun main(args: Array<String>) = runBlocking<Unit> {val jobs = List(100_000) {
launch {delay(1000L)print(".")
}}jobs.forEach { it.join() }
}
Example
Suspendsfor1second
fun main(args: Array<String>) = runBlocking<Unit> {val jobs = List(100_000) {
launch {delay(1000L)print(".")
}}jobs.forEach { it.join() }
}
Example
Wecanjoinajobjustlikeathread
fun main(args: Array<String>) = runBlocking<Unit> {val jobs = List(100_000) {
launch {delay(1000L)print(".")
}}jobs.forEach { it.join() }
}
Example
Trythatwith100kthreads!
Prints100kdotsafteroneseconddelay
fun main(args: Array<String>) = runBlocking<Unit> {val jobs = List(100_000) {
launch {delay(1000L)print(".")
}}jobs.forEach { it.join() }
}
Example
fun main(args: Array<String>) {val jobs = List(100_000) {
thread {Thread.sleep(1000L)print(".")
}}jobs.forEach { it.join() }
}
Example
fun main(args: Array<String>) {val jobs = List(100_000) {
thread {Thread.sleep(1000L)print(".")
}}jobs.forEach { it.join() }
}
Example
Exceptioninthread"main"java.lang.OutOfMemoryError:unabletocreatenewnativethread
JavainteropCanweuseKotlincoroutineswithJavacode?
Javainterop
CompletableFuture<Image> loadImageAsync(String name) { … }Java
CompletableFuture<Image> loadImageAsync(String name) { … }
CompletableFuture<Image> loadAndCombineAsync(String name1, String name2)
Java
ImagineimplementingitinJava…
CompletableFuture<Image> loadImageAsync(String name) { … }
CompletableFuture<Image> loadAndCombineAsync(String name1,String name2)
{CompletableFuture<Image> future1 = loadImageAsync(name1);CompletableFuture<Image> future2 = loadImageAsync(name2);return future1.thenCompose(image1 ->
future2.thenCompose(image2 ->CompletableFuture.supplyAsync(() ->
combineImages(image1, image2))));}
Java
CompletableFuture<Image> loadImageAsync(String name) { … }Java
fun loadAndCombineAsync(name1: String, name2: String
): CompletableFuture<Image> =…
Kotlin
CompletableFuture<Image> loadImageAsync(String name) { … }Java
fun loadAndCombineAsync(name1: String, name2: String
): CompletableFuture<Image> =future {
val future1 = loadImageAsync(name1)val future2 = loadImageAsync(name2)combineImages(future1.await(), future2.await())
}
Kotlin
CompletableFuture<Image> loadImageAsync(String name) { … }Java
fun loadAndCombineAsync(name1: String, name2: String
): CompletableFuture<Image> =future {
val future1 = loadImageAsync(name1)val future2 = loadImageAsync(name2)combineImages(future1.await(), future2.await())
}
Kotlinfuturecoroutinebuilder
CompletableFuture<Image> loadImageAsync(String name) { … }Java
fun loadAndCombineAsync(name1: String, name2: String
): CompletableFuture<Image> =future {
val future1 = loadImageAsync(name1)val future2 = loadImageAsync(name2)combineImages(future1.await(), future2.await())
}
Kotlin
CompletableFuture<Image> loadImageAsync(String name) { … }Java
fun loadAndCombineAsync(name1: String, name2: String
): CompletableFuture<Image> =future {
val future1 = loadImageAsync(name1)val future2 = loadImageAsync(name2)combineImages(future1.await(), future2.await())
}
Kotlin
BeyondasynchronouscodeKotlin’s approachtogenerate/yield– synchronouscoroutines
Fibonaccisequence
val fibonacci: Sequence<Int> = …
Fibonaccisequence
val fibonacci = buildSequence {var cur = 1var next = 1while (true) {
yield(cur) val tmp = cur + nextcur = nextnext = tmp
}}
Fibonaccisequence
val fibonacci = buildSequence {var cur = 1var next = 1while (true) {
yield(cur) val tmp = cur + nextcur = nextnext = tmp
}}
Acoroutinebuilderwithrestrictedsuspension
Fibonaccisequence
val fibonacci = buildSequence {var cur = 1var next = 1while (true) {
yield(cur) val tmp = cur + nextcur = nextnext = tmp
}}
Asuspendingfunction
Thesamebuildingblocks
public fun <T> buildSequence(builderAction: suspend SequenceBuilder<T>.() -> Unit
): Sequence<T> { … }
Thesamebuildingblocks
public fun <T> buildSequence(builderAction: suspend SequenceBuilder<T>.() -> Unit
): Sequence<T> { … }
Resultisasynchronous sequence
Thesamebuildingblocks
public fun <T> buildSequence(builderAction: suspend SequenceBuilder<T>.() -> Unit
): Sequence<T> { … }
Suspendinglambdawithreceiver
Thesamebuildingblocks
public fun <T> buildSequence(builderAction: suspend SequenceBuilder<T>.() -> Unit
): Sequence<T> { … }
@RestrictsSuspensionabstract class SequenceBuilder<in T> {
abstract suspend fun yield(value: T)}
Coroutineisrestrictedonlytosuspendingfunctionsdefinedhere
LibraryvsLanguageKeepingthecorelanguagesmall
Classicasync
async/awaitgenerate/yield Keywords
Kotlincoroutines
suspend Modifier
Kotlincoroutines
Standardlibrary
Kotlincoroutines
Standardlibrary
kotlinx-coroutines
launch,async,runBlocking,future,delay,
Job,Deferred,etc
http://github.com/kotlin/kotlinx.coroutines
ExperimentalinKotlin1.1&1.2
Coroutinescanbeused inproduction
Backwardscompatibleinside1.1&1.2
Tobefinalizedinthefuture
Thankyou
Anyquestions?
Slidesareavailableatwww.slideshare.net/elizarovemailmetoelizarov atgmail
relizarov