Котлин сопрограммы с возвращением значения

Я хочу создать метод сопрограммы, который имеет возвращаемое значение.

Например)

fun funA() = async(CommonPool) {
    return 1
}

fun funB() = async(CommonPool) {
    return 2
}

fun sum() {
    launch {
        val total = funA().await() + funB().await()
    }
}

Как мне поступить, если я хочу вернуть сумму в сумме?

лайк,

fun sum(): Int {
    launch {
        val total = funA().await() + funB().await()
    }   

    return total
}

7 ответов

Решение

Точно вернуть Intнужно выбраться из мира сопрограмм и вот что runBlocking для:

fun sum(): Int = runBlocking {
    funA().await() + funB().await()
}

См. Соединение блокирующих и неблокирующих миров в руководстве по сопрограммам и Составление функций приостановки, чтобы узнать, как это сделать, если вы хотите использовать sum внутри сопрограмм.

Возможно, уже поздно ответить на этот вопрос, но, надеюсь, кто-то найдет его полезным. Приведенный ниже фрагмент кода вычисляет сумму из 3 значений A + B + C. Каждое значение параллельно рассчитывается независимо в его собственном фоновом потоке, а затем все промежуточные результаты объединяются в один конечный результат и возвращаются в основной поток для отображения на экране.

Таким образом, для расчета окончательного значения требуется 5 секунд (не 10 секунд = 2 + 3 + 5), и, очевидно, результат равен 6, и он не блокируется, основной поток может обрабатывать другие события, пока выполнение sum() не завершено.

suspend fun sum(scheduler: ThreadPoolExecutor): Int = coroutineScope {

    withContext(scheduler.asCoroutineDispatcher()) {
        val a = async { funA() }
        val b = async { funB() }
        val c = async { funC() }

        a.await() + b.await() + c.await()
    }
}

fun funA(): Int {
    Thread.sleep(2000L)
    return 1
}

fun funB(): Int {
    Thread.sleep(3000L)
    return 2
}

fun funC(): Int {
    Thread.sleep(5000L)
    return 3
}

class MainActivity : AppCompatActivity(), View.OnClickListener {
    private val tripletsPool = ThreadPoolExecutor(3, 3, 5L, TimeUnit.SECONDS, LinkedBlockingQueue())

   ...

    override fun onClick(view: View?) {
        if (view == null) {
            return
        }

        when (view.id) {
            R.id.calculate -> {
                GlobalScope.launch(Dispatchers.Main, CoroutineStart.DEFAULT) {
                    progressBar.visibility = View.VISIBLE
                    result.setText("${sum(tripletsPool)}")
                    progressBar.visibility = View.GONE
                }
            }
        }
    }
}

Если я правильно понимаю ваш вопрос, вы хотите создать функцию ex."doWork()"который работает с сопрограммой и возвращает результат этой работы напрямую и лаконично, позволяя обновлять ваш пользовательский интерфейс, не замораживая его.

Таким образом, исходя из вышеизложенного:

Во-первых, я бы полностью избегал использованияrunBlocking, поскольку он в конечном итоге заблокирует поток пользовательского интерфейса или основной поток до тех пор, пока 'doWork()' не завершит свою работу в любом другом указанном вами потоке.

Я считаю, что наиболее подходящим решением для достижения вашей цели является использование Koltin Flow, поскольку вы сможете опубликовать прогресс позже, если захотите в будущем.

Вот как:

      fun doWork(): Flow<Int> =
        flow {
            //do long work 
           val sum:Int = doCalculations()
           emit(sum)
        }.flowOn(Dispatchers.Default)

Поток будет работать на сопрограмме по умолчанию.

И, на ваш взгляд, вы вызываете эту функцию:

      launch(Dispatchers.Main){
    doWork().single{
    val my result=it
    //Update your UI
    }
}

Другое более простое решение:

Сделать doWork() функцией приостановки

         suspend fun doWork(): Int =
          withContext(Dispatchers.Default){
                //do long work 
               val sum:Int = doCalculations()  
               return@withContext sum
}

И, на ваш взгляд, вы вызываете эту функцию:

      launch(Dispatchers.Main){
    val my result=doWork()
    //Update your UI
    }
}

Добавление другого способа достижения этого.

fun sum(): Int {
    var sum: Int = 0
    runBlocking {
        val jobA = async { funA() }
        val jobB = async { funB() }
        runBlocking{
           sum = jobA.await() + jobB.await()
        }
    }
    return sum
}

suspend fun funA(): Int {
    return 1
}

suspend fun funB(): Int {
    return 2
}

Вот еще один способ запустить funA()и funB()параллельно без использования runBlocking.

      fun funA() = CoroutineScope(Dispatchers.IO).async {
    delay(3000)
    return@async 1
}

fun funB() = CoroutineScope(Dispatchers.IO).async {
    delay(3000)
    return@async 2
}

fun sum() = CoroutineScope(Dispatchers.IO).async {
    val a = funA()
    val b = funB()
    return@async a.await() + b.await()
}

И если вы хотите бежать sum()без блокировки основного потока,

      CoroutineScope(Dispatchers.IO).launch {
    measureTimeMillis {
        Log.d("TAG", "sum=${sum().await()}")
    }.also {
        Log.d("TAG", "Completed in $it ms")
    }
}

Я редактирую вашу работу, я превращаю funA и funB в функцию suspend, и я создал функцию для оператора sum, и я вызываю основную функцию, вот пример:

suspend fun funA(): Int{
    return 1
}

suspend fun funB(): Int {
    return 2
}
fun sum() = runBlocking{
    val resultSum = async { funA.await() + funB.await() }
    return resultSum
}

fun main() = runBlocking{
    val result = async { sum() }
    println("Your result: ${result.await()}")
}

Надеюсь, это поможет

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

private var parentJob = Job()
private val coroutineContext: CoroutineContext get() = parentJob + Dispatchers.Main
private val scope = CoroutineScope(coroutineContext)

suspend fun removePhoneNumber(emailSets: EmailSets, personDetails: PersonDetails) : Boolean  {
    var successReturn = false
    scope.async(Dispatchers.IO) {
        val success = async {removePhoneNumbersAsync(emailSets,personDetails)}
        successReturn = success.await()

    }
    return successReturn
}

fun removePhoneNumbersAsync(emailSets: EmailSets, personDetails : PersonDetails):Boolean {
    var success = false
    try {
        val emailAddressContact = EmailAddressContact(emailSets.databaseId, personDetails.id, personDetails.active)
        repository.deleteEmailAddressContact(emailAddressContact)
        val contact = Contact(personDetails.id, personDetails.personName, personDetails.personPhoneNumber, 0)  
        repository.deleteContact(contact)
        success = true
    } catch (exception: Exception) {
        Timber.e(exception)
    }
    return success
}

В моей деятельности:

runBlocking {
    if (v.tag != null) {
            val personDetails = v.tag as PersonDetails
            val success  = viewModel.removePhoneNumber(emailSets,personDetails)
            if (success) {
                val parentView = v.parent as View
                (parentView as? TableRow)?.visibility = View.GONE
                val parentViewTable = parentView.parent as ViewGroup
                (parentViewTable as? TableLayout)
                parentViewTable.removeView(parentView)
            }
     }

}
Другие вопросы по тегам