I haven't read the article yet, but I do have something to contribute: several years ago I was ay PyCon and saw a talk in which someone mentioned async. I was interested and wanted to learn to use it. But I found there was no documentation at all! The syntax was briefly described, but not the semantics. I realized, years later, that the (non-)documentation was directed at people who were already familiar with the fea…
Python has had async for 10 years – why isn't it more popular?
281–290 of 305 posts
Re: Python has had async for 10 years – why isn't it more popular?
#282I learned about the concept of async/await from JS and back then was really amazed by the elegance of it. By now, the downsides are well-known, but I think Python's implementation did a few things that made it particularly unpleasant to use. There is the usual "colored functions" problem. Python has that too, but on steroids: There are sync and async functions, but then some of the sync functions can only be called f…
I think Python async is pretty cool - much nicer than threading or multiprocessing - yet has a few annoying rough edges like you say. Some specific issues I run into every time: Function colours can get pretty verbose when you want to write functional wrappers. You can end up writing nearly the exact same code twice because one needs to be async to handle an async function argument, even if the real functionality of…
I think that use case doesn't work well in async, because async effectively creates a tree of Promises that resolve in order. A task that doesn't get await-ed is effectively outside it's own tree of Promises because it may outlive the Promise it is a child of.
I think the solution would be something like Linux's zombie process reaping, and I can see how the devs prefer just not running those tasks to dealing with that mess.
Re: Python has had async for 10 years – why isn't it more popular?
#283Not too long ago, I read a comment on HN that suggested, due to Python's support for free-threading, async in Python will no longer be needed and will lose out to free-threading due to it's use of "colored" functions. Which seems to align with where this author ends up: > Because parallelism in Python using threads has always been so limited, the APIs in the standard library are quite rudimentary. I think there is an…
Interesting that very few people in that thread seem to understand Go's model, especially the author of this proposal. If you don't allow preemption, you still have a sort of coloring because most non async functions aren't safe to call in a virtual thread - they may block the executor. If you call C code, you need to swap out stacks and deal with blocking by potentially spawning more OS threads - that's what CGo doe…
If you choose a non-preemptive system, you naturally need yield points for cooperation. Those can either be explicit (await) or implicit (e.g. every function call). But you can get away with a minimal runtime and a stackless design.
Meanwhile, in a preemptive system you need a runtime that can interrupt other units of work. And it pushes you towards a stackful design.
All those decisions are downstream of the preemptive vs. cooperative.
In either case, you always need to be able to interface with CPU-heavy work. Either through preemption, or by isolating the CPU-heavy work.
Re: Python has had async for 10 years – why isn't it more popular?
#284The essential idea was I could be processing ~100 requests per vCPU in the async event loop while threading would max out 2-4 threads per CPU. Of course let us assume for either model we're waiting for 50-2000ms DB query or service call to finish before sending the response.
Is this not true? And if it is true, why isn't the juice is worth the squeeze: more than an order of magnitude more saturation/throughput for the same hardware and same language, just with a new engine at its heart?
Re: Python has had async for 10 years – why isn't it more popular?
#285Earlier quoted context omitted.
I've used gevent and I agree it works well. It has prevented me from even trying to experiment with the async/await syntax in Python for anything significant. However, gevent has to do its magic by monkeypatching. Wanting to avoid that, IIRC, was a significant reason why the async/await syntax and the underlying runtime implementation was developed for Python. Another significant reason, of course, was wanting to mak…
Would this result in surprises like if a function is turned to async by adding an await keyword, all of a sudden all functions that have it in their call stack become async
Similarly, a function that calls an async function wouldn't itself be async unless it also had the await keyword. But of course the usual way of calling an async function would be to await it. And calling it without awaiting it wouldn't return a value, just as with a generator; calling a generator function without yielding from it returns a generator object, and calling an async function without awaiting it would return a future object. You could then await the future later, or pass it to some other function that awaited it.
Re: Python has had async for 10 years – why isn't it more popular?
#286Earlier quoted context omitted.
I think Python async is pretty cool - much nicer than threading or multiprocessing - yet has a few annoying rough edges like you say. Some specific issues I run into every time: Function colours can get pretty verbose when you want to write functional wrappers. You can end up writing nearly the exact same code twice because one needs to be async to handle an async function argument, even if the real functionality of…
> I think if you create a task but don't await it (which is plausible in a server type scenario), it's not guaranteed to run because of garbage collection or something. I think that use case doesn't work well in async, because async effectively creates a tree of Promises that resolve in order. A task that doesn't get await-ed is effectively outside it's own tree of Promises because it may outlive the Promise it is a…
If you just do
async def myAsyncFunction():
...
await someOtherAsyncFunction()
...
then the call to someOtherAsyncFunction will not spawn any kind of task or delegate to the event loop at all - it will just execute someOtherAsyncFunction() within the task and event loop iteration that myAsyncFunction() is already running in. This is a major difference from JS.If you just did
someOtherAsyncFunction()
without await, this would be a fire-and-forget call in JS, but in Python, it doesn't do anything. The statement creates a coroutine object for the someOtherAsyncFunction() call, but doesn't actually execute the call and instead just throws the object away again.I think this is what triggers the "coroutine is not awaited" warning: It's not complaining about fire-and-forget being bad style, it's warning that your code probably doesn't do what you think it does.
The same pitfall is running things concurrently. In JS, you'd do:
task1 = asyncFunc1();
task2 = asyncFunc2();
await task1;
await task2;
In Python, the functions will be run sequentially, in the await lines, not in the lines with the function calls.To actually run things in parallel, you have to to
loop.create_task(asyncFunc())
or one of the related methods. The method will schedule a new task and return a future that you can await on, but don't have to. But that "await" would work completely differently from the previous awaits internally.Re: Python has had async for 10 years – why isn't it more popular?
#287Earlier quoted context omitted.
You make a very good case for why python's async isn't more prevalent, but I think this is not painting the full image. Taking a general case, let's say a forum, in order to render a thread one needs to search for all posts from that thread, then get all the extra data needed for rendering and finally send the rendered output to the client. In the "regular" way of doing this, one will compose a query, that will filte…
When I read your comment I was thinking: "But then you would need to structure your db in such a way that ... ahh yes, they are getting to that ... but then what about actually rendering the results? Ah they are describing that here ..." so well done I think. However, async tasks on a single core means potentially a lot of switching between those tasks. So async alone does not save the day here. It will have to be co…
Re: Python has had async for 10 years – why isn't it more popular?
#288Earlier quoted context omitted.
> I think if you create a task but don't await it (which is plausible in a server type scenario), it's not guaranteed to run because of garbage collection or something. I think that use case doesn't work well in async, because async effectively creates a tree of Promises that resolve in order. A task that doesn't get await-ed is effectively outside it's own tree of Promises because it may outlive the Promise it is a…
No, Python's system is more complex and unfortunately overloads "await" to do several things. If you just do async def myAsyncFunction(): ... await someOtherAsyncFunction() ... then the call to someOtherAsyncFunction will not spawn any kind of task or delegate to the event loop at all - it will just execute someOtherAsyncFunction() within the task and event loop iteration that myAsyncFunction() is already running in.…
If you do `someOtherAsyncFunction()` without await and Python tried to execute similarly to a version with `await`, then the one without await would happen in the same task and event loop iteration but there's no guarantee that it's done by the time the outer function is. Thus the existing task/event loop iteration has to be kept alive or the non-await'ed task needs to be reaped to some other task/event loop iteration.
> loop.create_task(asyncFunc())
This sort of intuitively makes sense to me because you're creating a new "context" of sorts directly within the event loop. It's similar-ish to creating daemons as children of PID 1 rather than children of more-ephemeral random PIDs.
Re: Python has had async for 10 years – why isn't it more popular?
#289Earlier quoted context omitted.
No, Python's system is more complex and unfortunately overloads "await" to do several things. If you just do async def myAsyncFunction(): ... await someOtherAsyncFunction() ... then the call to someOtherAsyncFunction will not spawn any kind of task or delegate to the event loop at all - it will just execute someOtherAsyncFunction() within the task and event loop iteration that myAsyncFunction() is already running in.…
I think this is semantically the same thing, though I'm sure your terminology is more correct (not an expert here). If you do `someOtherAsyncFunction()` without await and Python tried to execute similarly to a version with `await`, then the one without await would happen in the same task and event loop iteration but there's no guarantee that it's done by the time the outer function is. Thus the existing task/event lo…
As far as I understood it, calling an async function without await (or create_task()) does not run the function at all - there is no uncertainty involved.
Async functions work sort of like generators in that the () operator just creates a temporary object to store the parameters. The 'await' or create_task() are the things that actually execute the function - the first immediately runs it in the same task as the containing function, the second creates a new task and puts that in the event queue for later execution.
So
asyncFunc()
without anything else is a no-op. It creates the object for parameter storage ("coroutine object") and then throws it away, but never actually calls (or schedules) asyncFunc.When queuing the function in a new task with create_task(), then you're right - there is no guarantee the function would finish, or even would have started before the outer function completed. But the new task won't have any relationship to the task of the outer function at all, except if the outer function explicitly chooses to wait for the other task, using the Future object that was returned by create_task.
Re: Python has had async for 10 years – why isn't it more popular?
#290Earlier quoted context omitted.
Fair and accurate. But that’s pretty much what people use, right? I am happy to hear stories of using pypy or something to radically improve an architecture. I don’t have any from personal experience. I guess twisted and stackless, a long time ago.
The GIL is optional in new Python versions. Downsides are legacy library compatibility and degraded single thread performance.
Is this no longer true?