What will happen inside `suspendCancellableCorouti...
# coroutines
l
What will happen inside
suspendCancellableCoroutine
if a callback causes calling
continuation.resume()
twice? E.g.
Copy code
suspendCancellableCoroutine { continuation ->
            bleManager.startScan(
                onDeviceAppeared = { device ->
                        continuation.resume(Unit.right())
                    }
                },
                onScanError = {
                    continuation.resume(it.left())
                },
             )
        }
If I get a second
onDeviceAppeared
callback will the coroutine try to resume again?
s
It'll throw an exception: Continuations can only be resumed once.
l
Ok, so the correct solution is to remove the callback before resuming?
s
Yup, or guard it.
And you probably want to stop the scan when the coroutine has been cancelled as well....
l
Oh yes, good point. Thank you!
Guard would be a check
if (!resumed)
or something like this?
s
However, if onDeviceAppeared can occur more than once, you may want to consider using a Flow (callbackFlow { ... }) instead.
l
I just want to finish the scan after first device
I actually simplified the code, I have an
if (device.name == myName)
check there
s
Still, maybe a Flow could be good to use as the one that produces the scanned devices and then call
devicesFlow.first()
on it to get the first one.
l
Yes, that's also a good idea
Would move the whole completion/cancellation logic more into the consumer
s
Then you can filter the flow, combine, etc
Not really; the caller/consumer of the suspend function or of the Flow would govern the cancellation. Both scenarios are 'cold': Only active while someone is listening/consuming.
l
Sorry, I haven't made myself clear. I meant rather the stopping of the BLE scan
s
Ah... yup.
l
Also Flow would guarantee the
resume
cannot be called twice
s
You mean 'suspend' would guarantee that? A Flow's
emit
can be called many times.
l
1. In case of
suspendCancellableCoroutine
I filter for the specific device inside of the callback. That means, if
onDeviceAppeared
will be called twice for the same device, I would try to
resume
twice. 2. In case of a
callbackFlow
I would just emit the same device twice -> no problem, as the collection on consumer side probably already finished anyway (because we stopped collecting when we got the desired device).
So a
callbackFlow
is even easier to implement here.
s
Yup; and you can filter (eg filter on name) inside the callbackFlow, like you would in a suspendCancellableCoroutine, but also later in a
.filter
operator.
l
Yes, that's also much nicer.
Thanks for your helpful suggestions!
s
You're welcome 🙂