I'm not entirely sure what you want to achieve to be honest as your two examples point in slightly different directions:
Here's a way to structure the code so the channel is checked for messages every 10 mills no matter of there's ongoing async work or not: proc readWork() = var msg: Option[Msg] = data.hub.readMsg(Msg) while msg.isSome(): asyncSpawn processWork(msg) msg = data.hub.readMsg(Msg) const pollInterval = 10.millis while keepGoing(): waitFor sleepAsync(pollInterval) readWork() Run If you don't want the message queue to be checked while there is async work ongoing, the code becomes: proc readWork(): seq[Future[void]] = var msg: Option[Msg] = data.hub.readMsg(Msg) while msg.isSome(): result.add processWork(msg) msg = data.hub.readMsg(Msg) while keepGoing(): let work = readWork() if work.len > 0: for w in work: waitFor w else: waitFor sleepAsync(pollInterval) Run The `hasPendingOperations` approach, as well all above examples, are fundamentally flawed though, as is the idea to use `sleep` in general. This is part of the reason why chronos doesn't have a `hasPendingOperations` function: it promotes a poor pattern of execution that has many gotchas and flaws in general and if you're reaching for it something probably went wrong before you got to that point. Every time you introduce a sleep or other forms of polling to make things work, that's likely an inefficiency and the only time you would do that is when you don't have a choice (because you don't control the source channel for example or its notification mechanism is not adapted to the event loop). The way this is solved in efficient systems is that every message queue / channel comes with a signalling mechanism to tell the event loop to wake up because there is a new message to process. In chronos, this is `AsyncEvent` or `ThreadSignal` depending on whether it's a multithreaded scenario or not (the latter works for both) - basically, every time you put a message on the queue, you `fire` the event so the event loop wakes up: var signal = ThreadSignalPtr.new()[] proc addMessage(m: Msg) = data.hub.add m # Tell the loop there is new data signal.fireSync() proc readWork() = var msg: Option[Msg] = data.hub.readMsg(Msg) while msg.isSome(): asyncSpawn processWork(msg) msg = data.hub.readMsg(Msg) while keepGoing(): waitFor signal.wait() readWork() Run The above code is optimal in that there is no polling so the code will only do work when there's work to do - it'll also never sleep unnecessarily when it's already known that there's work arriving on the channel etc. Of course, you don't really want to be using `asyncCheck` / `asyncSpawn` in a production system due to the shoddy exception handling (unless you make sure to [catch all exceptions in each task](https://status-im.github.io/nim-chronos/error_handling.html#checked-exceptions)), but that's a different story.