Hacker News new | ask | show | jobs
by BiteCode_dev 1334 days ago
It gives good pointers but it falls short on the usual suspects for an article on asyncio.

When teaching it, it's important to emphasis:

- await is locally blocking, so you should isolate linear workflows into their own coro, which is the unit of concurrency.

- to allow concurrency, you should use asyncio.create_task on coro (formerly ensure_future).

- you should always explicitly delimitate the life cycle of any task. Right now, this means using something like gather() or wait(). TaskGroup will help when it becomes mainstream.

A HN comment is not great to explain that, but if you read the article, you should investigate those points. There is no good asyncio code without them, only pain and disapointment.

3 comments

> ... TaskGroup will help when it becomes mainstream.

Strongly agreed, but you can use anyio [1] in to of asyncio to get that functionality right now. Or, maybe even better, use Trio [2] instead, which is where the idea came from in the first place.

[1] https://anyio.readthedocs.io/en/stable/

[2] https://trio.readthedocs.io/en/stable/

> to allow concurrency, you should use asyncio.create_task on coro (formerly ensure_future).

This is misleading... you can use asyncio.gather which does this internally [0].

[0]: https://github.com/python/cpython/blob/main/Lib/asyncio/task...

Only if you wish to collect tasks where you schedule them, accumulate the results and don't need to limit concurrency.
Serious question, why would you limit concurrency in async world? You’re still on a single thread, why would you want to only schedule n things at a time?
> you should always explicitly delimitate the life cycle of any task

Unless you want a hacky actor system, in which case it's totally fine to `create_task` a ton of corountines which have their own spin loop with await sleep :)

Even if you want to ‘fire and forget’, it’s still essential to keep a reference to the task, otherwise it can be garbage collected mid-execution:

https://docs.python.org/3/library/asyncio-task.html#asyncio....

Wow! Did not know this, guess I’ve got a couple fixes to make…
One coroutine crashing and the others continuing to send it messages without noticing was my $40,000 bug.
I’m so confused by this architecture. It makes total sense in a threaded world but why would you want a coroutine constantly scheduling itself in a loop to pull messages off a queue like thing than just having the thing generating the message fire off a task to process it directly right there? It feels almost the same to me and then you can’t crash the coroutine.
At some point even those tasks must be cleanly stopped and unless you want to play erlang and "let it crash", the actors have a lifecycle as well. Making it explicit will avoid much pain, and ease testing a lot. Also it will make resources consumption more predictable.