3 ms·
Ok. This is what has been a huge hangup for me. It really seems that if you're doing asyncio, you must do EVERYTHING async, it's like asyncio takes over (infec
by bjt2n3904 4y ago
Ok. This is what has been a huge hangup for me.
It really seems that if you're doing asyncio, you must do EVERYTHING async, it's like asyncio takes over (infects?) the entire program.
- nrmitchi 4y agoYes. Unless you (and your team) are well-versed with python and how async works, and all the dependencies you use, is it very easy to accidentally do something blocking in async and lose most of the benefits.
- nicolaslem 4y agoTo be fair this is not really asyncio's fault, the same happens in most languages unless the runtime does some magic like scheduling blocking calls in a background thread (which can also be done with asyncio).
- brrrrrm 4y agoI feel the same way, it's a lot like `const` in C++. Basically it's enforcing usage for every upstream user to ensure there's no inefficiency (such as waiting for things like the network). JavaScript has an extremely nice "out" (I'm sure other languages do too): async function() { await async_thing() do_sync_stuff() } can be written function() { async_thing().then(do_sync_stuff) } which is quite intuitive and helps glue code together. I think the callback-based thinking really clarifies user intent ("just tell me when it's done") without impacting performance.
- gpderetta 4y agoproblem is async doesn't enforce anything. You can still block from an async function.
- quietbritishjim 4y agoThat's exactly the opposite of what it says really: if you need to do CPU stuff, then you can do that, it just won't be using asyncio. So it doesn't really infect your whole program. You could easily have, say, a thread to do all your asyncio stuff, another to do some CPU intenstive stuff (so long as it blocks the GIL) and yet another to do some blocking I/O e.g. interacting with a database with its own blocking APIs. In that case, you could spawn separate threads and exchange messages between them like usual. Where this falls down a little is if you want asyncio to infer your whole program - or more accurately, if you want to use it in all of the task-management code at the top level of your app. In that case, you would use asyncio's thread API [1] although it's a bit clunky for when you want to maintain state bound to a specific worker thread (like SQLite database objects). [1] https://docs.python.org/3/library/asyncio-task.html#running-in-threads https://docs.python.org/3/library/asyncio-task.html#running-...
- brrrrrm 4y agoI think the issue is more like this: 1. You jump into a huge codebase and find a very useful function you want to use in your code (it uses asyncio) 2. Your code doesn't use asyncio, so you start converting functions to be async (or else you can't use the `await` keyword) 3. It turns out your function is called by a bunch of different users, some of which do not use the asyncio runner 4. You end up having several meetings with upstream teams asking them to use the asyncio runner 5. A single team says "no" 6. Scrap the project and just rewrite that original function you found synchronously
- quietbritishjim 4y agoI guess I was focusing more on the CPU task problem mentioned at the top. But you're talking about blocking IO - more specifically, blocking IO that you want to convert to async (not just run in a worker thread using the thread-to-async API I mentioned). What you've written is true, but "infects" a smaller proportion of the code than you'd expect, in my experience. In fact, the better organised the program is, the less needs to change when converting to async. For example, reading from a connection your top-level async function would often be something like this: async def read_and_handle_messages(connection): while True: next_message_bytes = await connection.get_next_message() next_message_parsed = my_parser.parse(next_message_bytes) my_message_handler(next_message_parsed) All the application-specific code is in the parser and the message handler but neither of those are async. (If you need to send a response, your message handler could post a message to an async queue that is read by a separate writer task.) In your hypothetical scenario, you'd maybe write two a wrapper functions, one for blocking and one for async, which both read the message, parse it and handle it. But that only needs to be two versions of a three line function while all the subtantial code is completely shared. I find that there's very little actual async code in async programs that I write, even with no blocking IO historical baggage. Admittedly, that's partly because I organise programs to reach that goal, but it actually works out for the best because all the async-task lifecycle management ends up in one place, which makes following overall program flow particularly easy.
- acedTrex 4y agoYes async is viral, its all or nothing
- maxbond 4y agoAs someone who has written an application with sync and async Python components within the same process, it is all, nothing, or pain. (If you really, really need to do this - triple check - use queues & worker threads to decouple the sync parts and keep them out of your event loop thread, as a sort of sync-async impedence match.)
- evilduck 4y agoThe propagation/infection of async handling is just indicitive of the nature of the problem. Try writing async code in any other language and you see similar patterns emerge.
- jnwatson 4y agoThat's 80% due to the "what color is your function" problem. There are bridges from async to sync and vice versa, but they must be used very sparingly, as they aren't nestable.