Разработчики Kotlin называют корутины легковесными потоками. Однако такое объяснение не помогает понять их сути и даже мешает этому. На самом же деле корутины довольно простая, но мало похожая на потоки концепция.
РЕКОМЕНДУЕМ:
- Как сделать код на Kotlin более понятным
- Хорошие и плохие приемы программирования на Kotlin
- Полезные советы разработчику на языке Kotlin
Чтобы разобраться с корутинами (coroutine), надо понять, что такое routine. А это не что иное, как функция. Например, такая:
1 2 3 4 5 6 7 |
fun saveUserTasks(userId: Int) { val user = loadUser(userId) println("user loaded") val tasks = loadTasks(user) println("tasks loaded") saveTasks(tasks) } |
Две отличительные черты функций:
- они не имеют состояния и всегда запускаются «с чистого листа» (если, конечно, не используют глобальные переменные);
- функция должна завершить свое исполнение, перед тем как вернуть управление вызвавшему ее коду.
Корутина, с другой стороны, имеет состояние и может приостанавливать и возобновлять свое исполнение в определенных точках (возвращая, таким образом, управление еще до завершения своего исполнения).
Если мы попробуем вручную преобразовать приведенную выше функцию в корутину, то получим нечто вроде этого:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
class State( var label: Int = 0, var result: Any? = null ) fun saveUserTasks(userId: Int, state: State) { when (state.label) { 0 -> { val user = loadUser(userId) println("user loaded") state.result = user // Точка остановки исполнения } 1 -> { // Точка возобновления исполнения val user = state.result as User val tasks = loadTasks(user) println("tasks loaded") state.result = tasks // Точка остановки исполнения } 2 -> { // Точка возобновления исполнения val tasks = state.result as List<Task> saveTasks(tasks) } } } |
Теперь мы можем запустить нашу доморощенную «корутину» на выполнение с помощью такого кода:
1 2 3 4 5 6 |
fun main() { val state = State() saveUserTasks(7, state) saveUserTasks(7, state) saveUserTasks(7, state) } |
Результат будет тот же, что и в случае приведенной в начале классической функции. Но теперь у нас появилась возможность запускать и приостанавливать исполнение функции в нескольких точках. Если мы добавим сюда еще несколько подобных корутин, то сможем выполнять их фрагменты поочередно, создав иллюзию одновременного исполнения.
Именно так работают корутины в Kotlin. Он превращает функции с модификатором suspend в объект класса Continuation, который внутри представляет собой примерно такую же машину состояний, которую мы изобрели чуть выше. Точки остановки при этом появляются в местах вызова других suspend-функций.
Lets build a coroutine — хорошая статья, объясняющая на пальцах, как работают корутины в Kotlin и других языках.