---
title: "Discussion on \"WhileSubscribed(5000)\" | Hashnode"
description: "Discussion on \"WhileSubscribed(5000)\". Trampolining keeps the (blood) flow pumping!"
url: "https://hashnode.com/posts/whilesubscribed5000/630e96e456df9e8e502575df"
---

[Feed](https://hashnode.com/feed) /

Discussion

# [WhileSubscribed(5000)](https://blog.p-y.wtf/whilesubscribed5000?utm_source=hashnode&utm_medium=feed)

Trampolining keeps the (blood) flow pumping!

Aug 30, 2022 · 7 min read · blog.p-y.wtf  
Updated Mar 26  
Read the article

-   [#android](https://hashnode.com/tag/android)
-   [#jetpack-compose](https://hashnode.com/tag/jetpack-compose)
-   [#coroutines](https://hashnode.com/tag/coroutines)
-   [#kotlin](https://hashnode.com/tag/kotlin)
-   [#stream](https://hashnode.com/tag/stream)

## Written by

[Pierre-Yves Ricau](https://hashnode.com/@py)

@py · San Francisco

  
[View profile](https://hashnode.com/@py)

Deep dives

My pronouns are he / him. Android Engineer at Square / Block. A few things I enjoy: Publishing Open Source libraries (LeakCanary, Curtains, Radiography, square/logcat) Rollerblading Writing technical blogs (previously on dev.to/pyricau) Cooking Giving tech talks Reading sci-fi

## Responses 3

Markdown supported · ⌘↵ to post

[Sebastian Lobato Genco](https://hashnode.com/@sebaslogen)

[Oct 10, 2023](https://hashnode.com/posts/whilesubscribed5000/630e96e456df9e8e502575df/comment/65251e475965fddfa7ea4813)

Great article and discovery! Thanks a lot.

One thing I discovered testing this extensively is that Espresso and Robolectric automated tests tend to kick the count = 0 + your post() tricks earlier than Compose runs the first composition. Since Compose is ~100ms late to the party, tests fail. I solved it waiting (with a 1sec timeout) for a RESUME callback from a Compose listener.

[Akash Khunt](https://hashnode.com/@bastionkid)

[Sep 5, 2022](https://hashnode.com/posts/whilesubscribed5000/630e96e456df9e8e502575df/comment/6315ba47041b51e1712e7780)

Really neat solution 👏.

I just didn't understood one thing here that why we need both **postAtFrontOfQueue()** & **post()**? Wouldn't just **post()** do the trick for us like shown below? 🤔 With or without **postAtFrontOfQueue()** we essentially want to call **posted.complete(Unit)** at the end of the main thread queue.

```
Choreographer.getInstance().postFrameCallback {
    handler.post {
        posted.complete(Unit)
    }
}
```

[Shreyas Patil](https://hashnode.com/@patilshreyas)

[Aug 31, 2022](https://hashnode.com/posts/whilesubscribed5000/630e96e456df9e8e502575df/comment/630f1f4575af73ad1f0603ec)

Thanks, it's really great writeup 👍.

By the way, instead of `CompletableDeferred`, why don't you use `suspendCancellableCoroutine` by which you can also support cancelling registered callback if Flow is by chance terminated/cancelled/?

Let's say, this is how it looks if implemented with `suspendCancellableCoroutine`:

```
object WhileSubscribedOrRetained : SharingStarted {

    private val handler = Handler(Looper.getMainLooper())

    override fun command(subscriptionCount: StateFlow<Int>): Flow<SharingCommand> =
        subscriptionCount
            .transformLatest { count ->
                if (count > 0) {
                    emit(SharingCommand.START)
                } else {
                    awaitChoregrapherFramePostFrontOfQueue()
                    emit(SharingCommand.STOP)
                }
            }
            .dropWhile { it != SharingCommand.START }
            .distinctUntilChanged()

    override fun toString(): String = "WhileSubscribedOrRetained"

    @OptIn(InternalCoroutinesApi::class)
    private suspend fun awaitChoregrapherFramePostFrontOfQueue() {
        suspendCancellableCoroutine<Unit> { cont ->
            val frameCallback = Choreographer.FrameCallback {
                handler.postAtFrontOfQueue {
                    handler.post {
                        if (!cont.isCompleted) {
                            cont.resume(Unit)
                        }
                    }
                }
            }

            Choreographer.getInstance().postFrameCallback(frameCallback)

            cont.invokeOnCancellation {
                Choreographer.getInstance().removeFrameCallback(frameCallback)
            }
        }
    }
}
```
