Pull to refresh

Comments 36

На 2-м шаге, там где про рефакторинг, показав обновленный код на корутинах, Вы, как мне кажется, несколько слукавили. Ведь на RxJava тоже можно написать так:

Observable.zip(observable1, observable2, observable3, 
(t1, t2, t3) -> new Result(t1, t2, t3))
.subscribe(r -> /* handle result */, () -> /*handle completion*/,  e -> /* handle errors */)
Не слукавил, а черезчур упростил) Между t1 и t2, а таже t2 и t3 нужно было выполнять sideeffects. С корутинам это просто пара вставленных строчек в корутине.
Не могли бы Вы, пожалуйста, справедливости ради показать как будет выглядеть код с аналогичными сайд эффектами на корутинах?
try {
    val firstChunkJob = async { call1 } 
    val secondChunkJob = async { call2 } 
    val thirdChunkJob = async { call3 }
    val first = firstChunkJob.await()
    sideEffect1(first)
    val second = secondChunkJob.await()
    sideEffect2(second)
    val third = thirdChunkJob.await()
    return Result(first, second, third)
} catch (e: Exception) {
    // handle errors
}
Спасибо. На RxJava без вложенных вызовов так, пожалуй, не напишешь
Спасибо за статью!

Пять копеек к слову о Lifecycle Management: в JetBrains рекомендуют реализовывать интерфейс CoroutineScope и переопределять coroutine context.
Самый простой пример с официального сайта kotlinlang.org/docs/reference/coroutines/coroutine-context-and-dispatchers.html#cancellation-via-explicit-job:

class Activity : CoroutineScope {

    lateinit var job: Job

   override val coroutineContext: CoroutineContext
        get() = Dispatchers.Default + job

    fun create() {
        job = Job()
    }

    fun destroy() {
        job.cancel()
    }


В последнем релизе они захотели привести всё в порядок, поэтому мы не можем вызывать coroutine builders вроде launch() вне coroutine scope.

Для реализации lifecycle-aware jobs предлагается использовать GlobalScope
Верно, это подход, который случился в 0.26. Мигрируем на него)
А как быть с моей любимой библиотекой?
В ней есть метод

void foo(Runnable r);
suspend fun LibClazz.wrap() = suspendCancellableCoroutine<Unit> { continuation ->
      this.foo{
          continuation.resume(Unit)
      }
}
Я правильно понимаю что внутри я не смогу пользоваться всеми бонусами корутин и должен стартовать новую корутину?

При это если в пуле потоков для корутин закончатся потоки то вызов будет блокирующим?
Т.е. такой код убивает идею о том что можно запускать сколько угодно корутин потому что они дешевые.

"Внутри" — это где? Внутри вашей любимой библиотеки, конечно же, нет никаких корутин — но не потому что она ими "не может пользоваться", а потому что если бы у нее были корутины, то у нее был бы другой интерфейс.


При это если в пуле потоков для корутин закончатся потоки то вызов будет блокирующим?

Откуда взялся пул потоков для корутин? Приведенный вам пример кода не использует никаких дополнительных потоков.

Я непонятно выразился. Внутри блока (тела метода run в Runnable) передаваемого в качестве параметра.

Откуда взялся пул потоков для корутин?

Ну корутины же выполняются на каких-то потоках? Я не знаю точно как та все устроено, просто предположил что есть некий пул. Или они все в одном потоке работают?

Мой основной вопрос вот в чем: вызов suspendCancellableCoroutine блокирующий?
Что будет с остальными корутинами в этот момент? Они заблокируются до окончания работы этого метода?

Внутри тела метода run выполняется продолжение корутины, о чем можно догадаться по вызову continuation.resume(Unit). Конечно же внутри корутины можно пользоваться всеми бонусами корутин!


Вызов suspendCancellableCoroutine, как можно догадаться из названия, приостанавливает корутину. Приостановленная корутина не выполняется ни в каком потоке.


Остальные корутины тут ни при чем, suspendCancellableCoroutine влияет только на одну корутину.

Т.е. внутри тела метода run я могу написать
 pause(10000)
(не помню точно как называется метод) и это освободит текущий поток и он сможет выполнять другую корутину? Звучит здорово. Интересно как они этого добились. Надо по изучать.
Как они умудряются копировать стек и работает ли это с нативными методами.
Внутри метода run вы этого написать не можете. А вот после вызова wrap() — да, можете (при условии, что функция pause — приостанавливаемая).

Как они это сделали — документация не раскрывает, но подозреваю что все это сделано точно так же как во всех остальных языках сделан async/await, только синтаксис другой. Копирования стека при таком подходе не требуется.
Ну так не интересно :) так придется от кучи либ отказаться.

В общем игрушка крутая, но в таком виде очень ограниченная.
Потому что они не будут работать вместе с корутинами.

Любая либа которая использует блокирующие вызовы (а это значит все кто лезут в базу или отправляют/принимают запросы по сети или просто используют синхронизацию) просто заблокирует поток и корутины ни как не помогут.
Ну так против блокировки потока нет приёма. В Java попросту нет технической возможности обработать блокировку потока каким-то особым образом (кроме Project Loom).

Используйте асинхронные библиотеки вместо синхронных. Их уже достаточно наплодили, что-нибудь да найдёте.
Согласен, но при этом корутины применить не получится пока асинхронная библиотека не начнет их поддерживать, что случится примерно никогда.
Любую асинхронную библиотеку можно превратить в поддерживающую корутины путем написания некоторого количества оберток, и в комментариях выше показано как эти обертки будут выглядеть.
Только кто этим будет заниматься?
Заниматься чем? Написанием пяти строчек на функцию? Ну не знаю, наверное нужно подождать добровольца :-)
Что то у меня не заработало.

fun main(args: Array<String>) {
    GlobalScope.launch {
         delay(1000L)
        suspendCancellableCoroutine<Unit> { continuation ->
            CoolLibrary.doStuff {
                delay(1000L)
                println("World!") // print after delay
                continuation.resume(Unit)
            }
        }
    }
    println("Hello,")
    Thread.sleep(2000L) 
}


Ругается delay внутри doStuff:
Error:(11, 17) Kotlin: Suspension functions can be called only within coroutine body


EDIT
Из вашего комента понял что и не должно
И правильно ругается! Посмотрите внимательнее на тот код которым вам был предложен и на тот который вы написали. Откуда у вас взялись лишние вызовы функций в методе run?!

Третий раз вам говорю: все что делается в методе run — возобновляется корутина! Вызовы delay и println надо размещать уже в корутине.
Ну так я же не просто так этот Runnable передаю. В нем у меня бизнеслогика и хочется чтобы она продолжала работать, но с корутинами это не получится.
Четвертый раз повторяю: все получается. Только вызывать вашу бизнес-логику надо в другом месте!
Так вся суть моей либы в том что бизнеслогика должна быть внутри Runnable. И выбор у меня такой: использовать корутины или использовать либу.
И понятно что я выберу потому что «а кота этого я в первый раз вижу».
Погодите. Что ваша либа ожидает от переданного ей аргумента? Если она просто запускает переданный ей Runnable по окончанию своей работы — это одно, если она ждет когда метод run закончит работу — это другое.

Тут обсуждается решение только для первого случая.
У меня второй случай. Либа выполняет этот Runnable в том же потоке.
Тогда нужно было сразу признать, что код Tiberal вам не подходит…
Наверное.
Но я не мог этого сделать, потому что не знал всех ограничений которые к этому коду прилагаются (точнее я не был на сто процентов уверен что их не побороли).

А так-же хотел чтобы приверженцы корутин сами признали их ограниченность.
Именно поэтому вы держали в секрете постановку задачи? :-)
Нет. Я возможно не очень хорошо объяснил, но скрывать что либо не было моим намерением.
В примере CompositeJob
map.forEach { _ ,u -> u.cancel() }
хотел отметить, что в данном случае используется явовый forEach, доступный только в Java 8 (Android 5+). Чтобы заюзать именно котлиновый forEach для мапы, нужно обернуть параметры в скобки: map.forEach { (_ ,u) -> u.cancel() }
Это выглядит как деструктурирование, и им и является.
RxJava слишком сложный инструмент


Я просидел день, но не смог отрефакторить код, чтобы решить задачу.

ок
Sign up to leave a comment.