Live data from Hacker News

Python has had async for 10 years – why isn't it more popular?

tonybaloney.github.io

281–290 of 305 posts

Re: Python has had async for 10 years – why isn't it more popular?

#281
post #37

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…

Bad documentation is customary when writing Python

Re: Python has had async for 10 years – why isn't it more popular?

#282
post #70

I 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 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 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?

#283
post #88
post #61

Not 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…

I'm also surprised how often the preemptive vs. cooperative angle gets ignored in favor of the stackful vs stackless debate.

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?

#284
Not an expert but my chats with ChatGPT led me to believe async + FastAPI can give you 40x throughput for request handling over non-async code.

The 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?

#285
post #198

Earlier 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

It would work the same as it works now for generators. A function that calls a generator function isn't a generator just because of that; it only is if it also has the yield keyword in it (or yield from, which is a way of chaining generators).

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?

#286

Earlier 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…

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. 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?

#287
post #79

Earlier 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…

I think there is a bit of misunderstanding about what my post was about: it is not enough to make your app code async if you don't have all the infra to support that, hence why async in python didn't take the world by storm.

Re: Python has had async for 10 years – why isn't it more popular?

#288
post #286

Earlier 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.…

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 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?

#289
post #286

Earlier 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…

> but there's no guarantee that it's done by the time the outer function is.

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?

#290
post #90

Earlier 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.

I mentioned subinterpreters. That’s the thing that “makes the GIL optional,” you still have to use subinterpreters.

Is this no longer true?

Post reply on HN