с долгим процессом инициализации. И есть другой, Б, с кучей функций, который этот сервис под собой использует.
Каждая функция сервиса Б проверяет, готов ли А, и затем исполняется. Если не готов - запускает инициализацию и ждёт.
Я хочу, чтобы на время этой инициализации функции прервались, а сразу после - продолжили выполняться. Как это лучше всего реализовать?
Инициализация сервиса А - не suspend функция, но есть коллбэк об успешном завершении. Кажется, что Channel должен здесь помочь, но я не могу знать, какое количество функций ждёт инициализацию, чтобы именно столько значений туда допихать. Если только в какой-нибудь AtomicInteger переменной считать, но мне кажется, что должно быть решение поэлегантней
Верните результат инициализации в виде Deferred. Тогда все, кто будет делать ему await, то обудет ожидать, пока не завершится. И потом сразу все, что зависит стартанет. Не надо никаких каналов. Самый простой способ заворачивания колбэка такой: fun initialize(): Deferred<T>{ val res = CompletableDeferred<T>() startInitialization( callback = { result -> res.complete(result)} return res }
Выглядит классно! А если коллбек - это конкретный инстанс класса, который уже объявлен и используется всегда, есть ли какое-то более элегантное решение, чем оборачивание его в новый коллбек, который будет комплитить deferred и передавать управление?
При создании инстанса колбэка передать ему незавершенный деферред
он уже создан, то есть по сути это поле в классе, которое один раз создаётся. В принципе, могу переделать это поле в creator и при каждом обращении создавать новый инстанс
Не понял, а как вы на него реагировать хотите, если на эту реакцию никак подписаться нельзя?
ну вот думаю, как это лучше всего реализовать) Коллбек-обёрткой или коллбек-криэйтором. По сути +- одно и то же
Мой кейс немного усложнился и теперь это решение не подходит. Дело в том, что успешная инициализация может произойти в одной из всех параллельных попыток, и в этом случае все корутины должны продолжиться, а закомплитить я смогу только один deferred, который был передан конкретно коллбеку, который вызвал успешную инициализацию. Все остальные просто не дождутся ответа (или ответ будет а-ля "уже идет процесс инициализации"). Есть ли какие-то опции для такой ситуации?
Можно сделать несколько девередов и там по-моему есть готовый метод, который из нескольких выбирает певрый успешный, а остальные отменяет
а мне же надо, чтобы все успехом завершились, чтобы все ожидающие корутины продолжили выполняться
Не понял. Инициализация одна на всех или у каждого своя?
Попробую чуть подробнее. Есть сервис А, который нужно инициализировать. Доступа к его коду у меня нет. Метод init() принимает коллбек, который дёргается с кодом результата. Есть также мой сервис В, в котором несколько разных методов работают с инициализированным сервисом А. Все они могут параллельно обратиться к нему и потребовать инициализации, если она ещё не произошла. То есть, скажем, 3 корутины параллельно запросили инициализацию и ждут. Первая запустила, две другие получили ответ, что "инициализация уже в процессе" и всё (коллбек отработал и больше не вызовется). Я хочу, чтобы как только инициализация из первой корутины завершилась, все 3 корутины получили результат и продолжили выполнение
deferred подходит. я так понимаю это уже и советовали
еще раз. Один раз делаете метод инициализации, который возвращает кэшированный Deferred. Потом его можно раздать всем. private val result = CompletableDeferred<T>() private var initializationStarted = false public fun initialize(): Deferred<T>{ if(!initializationStarted) { initializeService(); initializationStarted = true} return result } Или какой-то вариант на тему.
А как с ним быть? По сути deferred-ы из двух корутин, которые вызвали init() после первой, сразу оборвутся без нужного результата. Сработает только deferred у первой
А, то есть кешировать deferred? Окей, спасибо, попробую такой вариант
+ только я бы initializationStarted засинхронизировал
Да, именно так. Или просто один раз его создать автоматом. Зависит от того, хотите вы его сбрасывать или нет.
Хочу, в теории может повторная инициализация потребоваться
Или AtomicBoolean просто использовать
ну тогда делаете его нуляблем и присваиваете в иницализации вместо того, чтобы булю выносить.
👌Спасибо большое, попробую!
Не нужно, если сам метод синхронизирован.
Обсуждают сегодня