Возврат из внутренней вложенной сопрограммы по метке Kotlin - PullRequest
0 голосов
/ 09 июля 2020

Случай использования: у меня есть много операций, которые я хочу выполнять асинхронно из основного потока, но также параллельно друг другу.

val scope = CoroutineScope(Dispatchers.IO)
val items = // List of items to do something.

scope.launch {
    items.forEach { item ->
        scope.launch {
            if (itemFailsValidation(item)) {
                // Here I want to skip this item but continue the forEach loop.
                return@launch // "There is more than one label with such a name in this" scope"
            }
            doSomethingThatMightTakeABit(item)
         }
     }
}

Если я попытаюсь добавить метку, например inner@scope.launch, редактор говорит: «Ярлык является избыточным, потому что на него нельзя ссылаться ни в '' break '', '' 'continue' ', и в' 'return' 'выражении"

Кто-нибудь знает хороший способ делать это?

Ответы [ 2 ]

2 голосов
/ 09 июля 2020

Если нам нужно вернуться из лямбда-выражения, мы должны пометить его и квалифицировать return . Чтобы ваш случай вернулся из внутренней сопрограммы:

scope.launch {
    items.forEach { item ->
        scope.launch innerScope@ {
            if (itemFailsValidation(item)) {
                return@innerScope
            }
            doSomethingThatMightTakeABit(item)
        }
    }
}

Но мы можем упростить ваш случай и переписать код без использования метки:

scope.launch {
    items.forEach { item ->
        if (!itemFailsValidation(item)) {
            scope.launch { doSomethingThatMightTakeABit(item) }
        }
    }
}

// OR

items.forEach { item ->
    if (!itemFailsValidation(item)) {
        scope.launch { doSomethingThatMightTakeABit(item) }
    }
}    

Если вы хотите дождаться, пока все сопрограммы перейдут в fini sh и сделайте что-нибудь в потоке пользовательского интерфейса:

scope.launch(Dispatchers.Main) {
    processItemsInBackground()

    // update UI after processing is finished
}

suspend fun processItemsInBackground() = withContext(Dispatchers.IO) {
    // withContext waits for all children coroutines
    items.forEach { item ->
        if (!itemFailsValidation(item)) {
            launch { doSomethingThatMightTakeABit(item) }
        }
    }
}
1 голос
/ 09 июля 2020

Вы можете изменить имя лямбды с помощью ключевого слова name@.

Пример:

scope.launch outerCoroutine@ {
    items.forEach { item ->
        scope.launch {
            if (itemFailsValidation(item)) {
                return@outerCoroutine
            }
            doSomethingThatMightTakeABit(item)
         }
     }
}

Эта функция не задокументирована должным образом, но некоторые документы, такие как This Выражения имеют демонстрационное использование, и некоторые функции, определенные в стандартной библиотеке, используют его.

Изменить: Это фактически задокументировано в Return and Jumps .

...