How does asyncio.sleep work with negative values?
<p>I decided to implement sleep sort (<a href="https://rosettacode.org/wiki/Sorting_algorithms/Sleep_sort" rel="nofollow">https://rosettacode.org/wiki/Sorting_algorithms/Sleep_sort</a>) using Python's <code>asyncio</code> when I made a strange discovery: it works with negative values (and returns immediately with 0)!</p>

<p>Here is the code (you can run it here <a href="https://repl.it/DYTZ" rel="nofollow">https://repl.it/DYTZ</a>):</p>

<pre><code>import asyncio
import random

async def sleepy(value):
    return await asyncio.sleep(value, result=value)


async def main(input_values):
    result = []
    for sleeper in asyncio.as_completed(map(sleepy, input_values)):
        result.append(await sleeper)
    print(result)


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    input_values = list(range(-5, 6))
    random.shuffle(input_values)
    loop.run_until_complete(main(input_values))
</code></pre>

<p>The code takes 5 seconds to execute, as expected, but the result is always <code>[0, -5, -4, -3, -2, -1, 1, 2, 3, 4, 5]</code>. I can understand 0 returning immediately, but how are the negative values coming back in the right order?</p>

<p>If you take a look at the asyncio source, <code>sleep</code> <a href="https://github.com/python/cpython/blob/ce83a8c892ff17dc5eaba2420854d82589b269cd/Lib/asyncio/tasks.py#L505-L507" rel="nofollow">special cases 0</a> and returns immediately.</p>

<pre><code>if delay == 0:
    yield
    return result
</code></pre>

<p>If you continue through the source, you'll see that any other value gets passed through to the event loop's <code>call_later</code> method. Looking at how <code>call_later</code> is implemented for the default loop (<code>BaseEventLoop</code>), you'll see that <code>call_later</code> <a href="https://github.com/python/cpython/blob/ce83a8c892ff17dc5eaba2420854d82589b269cd/Lib/asyncio/base_events.py#L463" rel="nofollow">passes a time to <code>call_at</code></a>.</p>

<pre><code>self.call_at(self.time() + delay, callback, *args)
</code></pre>

<p>The reason the values are turned in order is that the times created with negative delays occur before those with positive delays.</p>

python sorting sleep python-asyncio