procedimentos) que concordam em parar sua execução, permitindo que outra rotina possa ser executada naquele momento e esperando que essa rotina secundária devolva a execução para ela em algum momento • Dessa forma, uma rotina coopera com a outra • Criada para dar a sensação de execução paralela em sistemas com um processador e apenas uma thread
no meio, não precisamos mais chamar o yield (que é uma Suspend Function); qualquer chamada para uma Suspend Function pode parar a execução de uma Coroutine • Funcionam utilizando Continuations para guardar o estado da execução
representado pelo valor da CoroutineContext, que é definido na própria biblioteca • O contexto das Coroutines pode ser pensado como um agrupamento de alguns elementos. Os principais elementos são o Job e o Dispatcher
ciclo de vida que culmina em sua completude • Toda Coroutine roda em um Job. Você pode especificar um ou um novo será criado quando uma nova Coroutine for lançada. O Job é utilizado principalmente para fazer a junção de Coroutines e cancelamentos
a Coroutine utiliza para sua execução. O Dispatcher pode confinar a execução a uma Thread específica, disparar a um ThreadPool, ou deixar ela rodar sem confinamento (que normalmente não é utilizado) • Dispatcher.Default: Utiliza um pool comum de thread. Deve ser utilizado para tarefas com computação intensiva • Dispatcher.IO: Utiliza um pool compartilhado de thread criado sob demanda, que foi feito para operações bloqueantes • Dispatcher.Main: Apenas para Android, serve para executar a Coroutine na main thread (atualizar UI)
spawns two other jobs, one with GlobalScope GlobalScope.launch { println("job1: I run in GlobalScope and execute independently!") delay(1000) println("job1: I am not affected by cancellation of the request") } // and the other inherits the parent context launch { delay(100) println("job2: I am a child of the request coroutine") delay(1000) println("job2: I will not execute this line if my parent request is cancelled") } } delay(500) request.cancel() // cancel processing of the request delay(1000) // delay a second to see what happens println("main: Who has survived request cancellation?")
passado como parâmetro, suspendendo a execução atual até que o bloco complete sua execução e retorne seu resultado • Muito utilizado em Android para executar operações em background e atualizar a UI com o resultado
como um Deferred. A Coroutine que está rodando será cancelada caso o resultado do Deferred seja cancelado • A Coroutine gerada no Async tem uma diferença chave comparada com outras primitivas em outras linguagens e frameworks: ela cancela o Job pai em falha para garantir um paradigma estruturado de concorrência
estados: trancado e aberto • É não reentrante; isso significa que chamar o lock da mesma thread/Coroutine que atualmente tem a chave ainda faz com que o invocante fique suspendido
diferenças chave entre eles: • Ao invés da operação bloqueante put, ele tem a operação send, que suspende a Coroutine • Ao invés da operação bloqueante take, ele tem o receive, que suspende a Coroutine • É possível implementar back pressure para Coroutines com Pipelines (Channels que podem emitir infinitos valores) Channels
= produceNumbers() // produces integers from 1 and on val squares = square(numbers) // squares integers for (i in 1..5) println(squares.receive()) // print first five println("Done!") // we are done coroutineContext.cancelChildren() // cancel children coroutines } fun CoroutineScope.produceNumbers() = produce<Int> { var x = 1 while (true) send(x++) // infinite stream of integers starting from 1 } fun CoroutineScope.square(numbers: ReceiveChannel<Int>): ReceiveChannel<Int> = produce { for (x in numbers) send(x * x) } 1 4 9 16 25 Done!