Stanislav Kral
10/11/2023, 9:13 PMsealed interface Event {
object TransactionStarted: Event
object TransactionPrintingStarted: Event
class TransactionPrintingError(val retry: () -> Unit): Event
}
• and a method that initiates a transaction and prints the result
suspend startTransaction() = flow {
emit(Event.TransactionStarted)
// ...
emit(Event.TransactionPrintingStarted)
// start printing, may return error
// print error occurs
val error = "Out of paper" // received from a print method
// how to give the flow's collector a chance to retry printing?
emit(Event.TransactionPrintingError({
// retry callback invoked
// ... print again
}))
// suspend here to not cancel the flow?
// retry callback may be invoked
}
How do I wait for a callback from an object sent via emit
?
I've tried using suspendCancellableCoroutine
but that didn't work, since I can't call emit
inside the body.
Thanks!Francesc
10/11/2023, 9:22 PMflow
should not be `suspend`ingStanislav Kral
10/11/2023, 9:52 PMstartTransaction
and printTransactionResult
as high-level methods. In this flow I'd like to combine the general requirement of a transaction flow (perform a TX and print its result). This flow contains the following logic:
• perform tx
• check result
• if unsucessful abort (don't print at all)
• if successful, print the result
• if print fails, give the consumer an option to retry printing
Having it coupled together in a flow is a thing I desire, as it may be reused multiple times without the user having to implement the logic each time.
Thanks for pointing out that functions returning a flow
should not be `suspend`ingFrancesc
10/11/2023, 9:56 PMretry
to the flow and have it restart on failure.Stanislav Kral
10/11/2023, 10:07 PMFrancesc
10/11/2023, 10:18 PMsuspendCoroutine
or suspendCancelableCoroutine
and then loop inside your flow.emit
inside the suspendCoroutine
method, you'd return a value and then you'd emit that value in the flowfun myFlow(): Flow<Result> = flow {
// emit other stuff
val result = foo()
if (result) emit(Result.Success)
// other stuff
}
suspend fun foo() = suspendCancellableCoroutine<Boolean> { cont ->
val callback = object : SomeInterface {
override fun onSuccess() {
cont.resume(true)
}
override fun onFailure() {
cont.resume(false)
}
}
api.register(callback)
cont.invokeOnCancellation { api.unregister(callback }
}
Patrick Steiger
10/12/2023, 5:16 AMStanislav Kral
10/12/2023, 6:12 AM// store locally
val txResult = performTx()
fun print() {
if (txResult.success) {
val printResult = printTxResult(txResult)
if (!printResult) {
displayPrintError = true
}
}
}
print()
// somewhere in UI layer
if (displayPrintError) {
showPrintErrorDialog(
onDismiss = {
print()
}
)
}
A bidirectional communication with the flow, as Patrick suggested, would make it work, as I would be able to reuse that piece of logic elsewhere.
Note that printing a receipt after a TX is finished is in most cases mandatory (except for a critical printer failure)- that's why I'd like to couple it with starting a transaction and inspecting its result.Patrick Steiger
10/12/2023, 6:07 PMval retryCmnds = MutableSharedFlow<Unit>()
val txflow = flow {
something()
}.flatMapLatest {
retryCmnds.onStart { emit(Unit) }.map {
print()
}
}
fun retry() {
launch { retryCmnds.emit(Unit)
}
Stanislav Kral
10/13/2023, 7:17 AMMutableSharedFlow
, I've tried doing something like this:
flow {
// perform tx, emit multiple events
// ...
emit(Event.TransactionFinished)
// notify that receipt is being printed
emit(Event.PrintingReceipt)
val retryFlow = MutableSharedFlow<Boolean>()
while (true) {
val error = printResult()
if (error == null) {
emit(Event.PrintFinished)
return@flow
}
emit(Event.PrintFailed(
error = error,
retryCallback = {
retryFlow.tryEmit(true)
}
))
val retry = retryFlow.first() && currentCoroutineContext().isActive
if (!retry) {
return@flow
}
}
}
Are there any obvious mistakes?