Skip to content

Conversation

@Denis-Iuferov
Copy link

No description provided.

Copy link

@sergeykamilyevich sergeykamilyevich left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

привет! оставил пару комментариев


class CatsViewModel(private val catsService: CatsService) : ViewModel() {
private val _uiModelFlow = MutableStateFlow<Result>(Result.Loading)
val uiModelFlow: StateFlow<Result> = _uiModelFlow

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

лучше в конце добавлять .asStateFlow() чтобы нельзя было скастить к MutableStateFlow

sealed interface Result {
data class Success(val uiModel: CatsUiModel) : Result
data class Error(val message: String?) : Result
object Loading : Result

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

минорно, для удобства лучше сделать data object

Copy link
Author

@Denis-Iuferov Denis-Iuferov Aug 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Только ради генерации toString()? Ну да, хуже не будет.) Но для этого надо котлин поднять в проекте, так что пожалуй не буду

private val _uiModelFlow = MutableStateFlow<Result>(Result.Loading)
val uiModelFlow: StateFlow<Result> = _uiModelFlow
private val exceptionHandler = CoroutineExceptionHandler { _, e ->
when (e) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

в exceptionHandler лучше не обрабатывать ошибки, а только делать логирование, что и указано в условиях задачи, сюда должны попасть неперехваченные в try-catch ошибки

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Тогда стэйт ошибки не дойдет никогда до ui. Либо внутри async ловить ошибки, тогда они не дойдут до exceptionHandler-а, чтобы там залогироваться. Либо давать им уходить в exceptionHandler и если там только логировать, то стэйт не обновиться.

viewModelScope.coroutineContext.cancelChildren()
viewModelScope.launch(exceptionHandler) {
_uiModelFlow.emit(Result.Loading)
val factJob = async(Dispatchers.IO) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

хорошая практика передавать Dispatchers через конструктор вьюмодели, чтобы его можно было подменять (например в тестах)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не спорю, не было такой задачи

null
}
} catch (e: Throwable) {
ensureActive()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

тут эта проверка не имеет особого смысла, лучше в блоке when сделать проброс CancellationException дальше:
if (e is CancellationException) throw e

Copy link
Author

@Denis-Iuferov Denis-Iuferov Aug 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"ensureActive()" короче чем "if (e is CancellationException) throw e" и делает примерно тоже самое

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ensureActive кидает новый эксепшн с новым стек трейсом, а throw e пробрасывает дальше исходный

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Так это новый CancellationException, какой там стэктрэйс нужен?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

перехватывать и подменять CancellationException в данном случае совершенно неоправданная практика, ты потеряешь оригинальное сообщение, причину и место выброса

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ок, хотя я не до конца понимаю, где эта инфа может понадобиться

} else {
null
}
} catch (e: Throwable) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

нет обработки SocketTimeoutException

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А там четко не сказано, что это надо для картинок и что вообще должно происходить, когда ошибка только в одном из запросов. Какое условие такой и результат

}

scope.launch {
val fact = factJob.await()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

тут не выполнено требование одновременной отмены запросов, и launch и оба async запущены на одной SupervisorJob и отмена одной загрузки никак не повлияет на другую

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Там нет такого требования там написано: "Отменятся запросы должны одновременно". Там не написано отменять оба запроса в случае ошибки в любом из них. Отменяю я запросы при закрытии экрана вместе с гибелью скоупа. Это происходит одновременно

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants