es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

¿Cómo puedo refactorizar este método de Kotlin con coroutines para que se ejecuten correctamente?

Toma este método

override suspend fun login(email: String, password: String): BaseResult<unit> {
    coroutineScope {
        try {
            fetchMobileConfig()
        } catch (e: Exception) {
            // Sin manejar porque parece ser un proceso independiente.
        }
    }

    val loginRequest = LoginRequest(
        email = email,
        password = password
    )

    return when (val response = loginNetwork.login(loginRequest)) {
        is NetworkResponse.Success -> {
            coroutineScope {
                launch {
                    getUserData()
                }
            }

            callSomeFinishingFunctionThatIsSuspending()

            BaseResult.Ok(Unit)
        }
        is NetworkResponse.Error.Unauthorised -> {
            BaseResult.Error(response.exception)
        }
    }
}

Me encontré con este método y quería refactorizarlo. Tengo algunas ideas, pero quiero ver si hay algo mejor que no haya considerado. En realidad, parece que funciona, pero se siente un poco caótico.

El método se llama desde un ViewModel con viewModelScope{}.

Las funciones fetchMobileConfig() y getUserData() son un poco extrañas, pero actualmente parecen ser métodos independientes. Básicamente, si fallan, el inicio de sesión no debería fallar.

El BaseResult devuelto se utiliza para informar al ViewModel del estado. Si te preguntas cómo se llama al método de inicio de sesión en loginNetwork, se ve así:

override suspend fun login(loginRequest: LoginRequest): NetworkResponse<sessiontoken> {
    return performRequest {
        loginService.login(loginRequest)
    }
}

Estoy satisfecho con el funcionamiento actual.

¿Cuál sería una forma mejor de escribir esta función a nivel de repositorio? Estoy abierto a otra estructura completamente diferente para ello.

Tags:  , ,

Answer

  1. Avatar for davy.ai

    Después de revisar el código proporcionado, aquí están algunas sugerencias para refactorizar:

    1. Utiliza try-catch en lugar de manejar la excepción con comentarios. Cuando ocurre una excepción dentro de la función fetchMobileConfig(), se ignore de forma silenciosa. Al envolver la llamada a la función dentro de un bloque try-catch, puedes manejar la excepción de manera más elegante.
    2. Utiliza la concurrencia estructurada en lugar de lanzar nuevas corrutinas dentro de la función login. Al utilizar viewModelScope.launch en tu ViewModel y llamar a la función login desde ahí, puedes asegurarte de que todas las corrutinas se cancelen cuando se destruya el ViewModel.

    3. Considera pasar las dependencias como parámetros en lugar de depender del estado global. La función login actualmente está llamando a funciones que están definidas fuera de su ámbito (fetchMobileConfig() y getUserData()). Al pasar estas funciones como parámetros, puedes hacer que las dependencias sean más explícitas y más fáciles de administrar.

    Aquí tienes un ejemplo de código refactorizado utilizando las sugerencias anteriores:

    suspend fun login(
        email: String,
        password: String,
        fetchMobileConfig: suspend () -> Unit,
        getUserData: suspend () -> Unit
    ): BaseResult<unit> {
        try {
            fetchMobileConfig()
        } catch (e: Exception) {
            // Manejar la excepción de manera elegante
        }
    
        val loginRequest = LoginRequest(email = email, password = password)
    
        return when (val response = loginNetwork.login(loginRequest)) {
            is NetworkResponse.Success -> {
                withContext(Dispatchers.IO) {
                    getUserData()
                }
    
                llamarAlgunaFuncionFinalizadoraQueEstéEnSuspensión()
    
                BaseResult.Ok(Unit)
            }
            is NetworkResponse.Error.Unauthorised -> {
                BaseResult.Error(response.exception)
            }
        }
    }
    

    Al hacer que las funciones fetchMobileConfig() y getUserData() sean parámetros explícitos, las dependencias son más claras. Ahora se utiliza el bloque withContext(Dispatchers.IO) para llamar a getUserData() en lugar de lanzar una nueva corrutina. Esto garantiza que getUserData() se ejecute en el hilo de entrada/salida (IO), lo cual es preferible para las operaciones en red.

Comments are closed.