What is the core difference between asyncio and trio?
Asked Answered
O

1

128

Today, I found a library named trio which says itself is an asynchronous API for humans. These words are a little similar with requests'. As requests is really a good library, I am wondering what is the advantages of trio.

There aren't many articles about it, I just find an article discussing curio and asyncio. To my surprise, trio says itself is even better than curio(next-generation curio).

After reading half of the article, I cannot find the core difference between these two asynchronous framework. It just gives some examples that curio's implementation is more convenient than asyncio's. But the underlying structure is almost the same.

So could someone give me a reason I have to accept that trio or curio is better than asyncio? Or explain more about why I should choose trio instead of built-in asyncio?

Ole answered 26/3, 2018 at 2:7 Comment(10)
You don't have to accept that it's better. Who said you did?Paregmenon
Also, if the library's interface is more convenient for some of the examples (I assume you meant interface, because what would it mean for the implementation to be more convenient?), that's probably exactly the way in which it's better. In the same way that requests makes a lot of things convenient that are doable but painful with urllib. Of course "more convenient" is subjective, but then this question is subjective in the first place.Paregmenon
@Paregmenon As a developer, we have to absorb new things before they become popular, otherwise we will lose advantages. Make sense :) ?Ole
@Paregmenon I agree with you in this part, but the article says there are some advantages on framework design(Or structure)? I want to understand.Ole
We only have to absorb new things before they become popular if they're actually useful or interesting to us. Especially since the vast majority of new things are never going to be popular, and there's not enough time in the day to learn even a tiny fraction of them.Paregmenon
@Paregmenon Yes, that make sense. So I want to understand if it is actually useful.Ole
AIUI, the main point of curio is that by stripping things down so the public API is just tasks (rather than tasks and coroutines and futures plus an optional callback API), you lose some functionality that's sometimes useful, but make it easier to build a whole bunch of "sugar on top" that adds back in more than you've lost. It looks like trio (which I've never used) is basically that whole bunch of sugar. Which is cool. If you like the curio design but want to compose tasks in ways that take a few lines of non-trivial code, I'd probably use trio. If you want a future, stay away.Paregmenon
@Paregmenon Thanks a lot, I think I should have a look at trio's design principle. And I will leave this question open to wait more answers.Ole
The design principles are right there in the docs you linked to. And I can't see what answer anyone could give beyond linked to those same docs you already have, or adding subjective opinions on top of it, neither of which is appropriate as a SO answer. I don't think the question is downvotable, but I don't think it's answerable, either.Paregmenon
My reason to choose trio: it's much easier for me to understand and reason about than the transports & protocols soup.Garwin
M
238

Where I'm coming from: I'm the primary author of trio. I'm also one of the top contributors to curio (and wrote the article about it that you link to), and a Python core dev who's been heavily involved in discussions about how to improve asyncio.

In trio (and curio), one of the core design principles is that you never program with callbacks; it feels more like thread-based programming than callback-based programming. I guess if you open up the hood and look at how they're implemented internally, then there are places where they use callbacks, or things that are sorta equivalent to callbacks if you squint. But that's like saying that Python and C are equivalent because the Python interpreter is implemented in C. You never use callbacks.

Anyway:

Trio vs asyncio

Asyncio is more mature

The first big difference is ecosystem maturity. At the time I'm writing this in March 2018, there are many more libraries with asyncio support than trio support. For example, right now there aren't any real HTTP servers with trio support. The Framework :: AsyncIO classifier on PyPI currently has 122 libraries in it, while the Framework :: Trio classifier only has 8. I'm hoping that this part of the answer will become out of date quickly – for example, here's Kenneth Reitz experimenting with adding trio support in the next version of requests – but right now, you should expect that if you're trio for anything complicated, then you'll run into missing pieces that you need to fill in yourself instead of grabbing a library from pypi, or that you'll need to use the trio-asyncio package that lets you use asyncio libraries in trio programs. (The trio chat channel is useful for finding out about what's available, and what other people are working on.)

Trio makes your code simpler

In terms of the actual libraries, they're also very different. The main argument for trio is that it makes writing concurrent code much, much simpler than using asyncio. Of course, when was the last time you heard someone say that their library makes things harder to use... let me give a concrete example. In this talk (slides), I use the example of implementing RFC 8305 "Happy eyeballs", which is a simple concurrent algorithm used to efficiently establish a network connection. This is something that Glyph has been thinking about for years, and his latest version for Twisted is ~600 lines long. (Asyncio would be about the same; Twisted and asyncio are very similar architecturally.) In the talk, I teach you everything you need to know to implement it in <40 lines using trio (and we fix a bug in his version while we're at it). So in this example, using trio literally makes our code an order of magnitude simpler.

You might also find these comments from users interesting: 1, 2, 3

There are many many differences in detail

Why does this happen? That's a much longer answer :-). I'm gradually working on writing up the different pieces in blog posts and talks, and I'll try to remember to update this answer with links as they become available. Basically, it comes down to Trio having a small set of carefully designed primitives that have a few fundamental differences from any other library I know of (though of course build on ideas from lots of places). Here are some random notes to give you some idea:

A very, very common problem in asyncio and related libraries is that you call some_function(), and it returns, so you think it's done – but actually it's still running in the background. This leads to all kinds of tricky bugs, because it makes it difficult to control the order in which things happen, or know when anything has actually finished, and it can directly hide problems because if a background task crashes with an unhandled exception, asyncio will generally just print something to the console and then keep going. In trio, the way we handle task spawning via "nurseries" means that none of these things happen: when a function returns then you know it's done, and Trio's currently the only concurrency library for Python where exceptions always propagate until you catch them.

Trio's way of managing timeouts and cancellations is novel, and I think better than previous state-of-the-art systems like C# and Golang. I actually did write a whole essay on this, so I won't go into all the details here. But asyncio's cancellation system – or really, systems, it has two of them with slightly different semantics – are based on an older set of ideas than even C# and Golang, and are difficult to use correctly. (For example, it's easy for code to accidentally "escape" a cancellation by spawning a background task; see previous paragraph.)

There's a ton of redundant stuff in asyncio, which can make it hard to tell which thing to use when. You have futures, tasks, and coroutines, which are all basically used for the same purpose but you need to know the differences between them. If you want to implement a network protocol, you have to pick whether to use the protocols/transports layer or the streams layer, and they both have tricky pitfalls (this is what the first part of the essay you linked is about).

Trio's currently the only concurrency library for Python where control-C just works the way you expect (i.e., it raises KeyboardInterrupt where-ever your code is). It's a small thing, but it makes a big difference :-). For various reasons, I don't think this is fixable in asyncio.

Summing up

If you need to ship something to production next week, then you should use asyncio (or Twisted or Tornado or gevent, which are even more mature). They have large ecosystems, other people have used them in production before you, and they're not going anywhere.

If trying to use those frameworks leaves you frustrated and confused, or if want to experiment with a different way of doing things, then definitely check out trio – we're friendly :-).

If you want to ship something to production a year from now... then I'm not sure what to tell you. Python concurrency is in flux. Trio has many advantages at the design level, but is that enough to overcome asyncio's head start? Will asyncio being in the standard library be an advantage, or a disadvantage? (Notice how these days everyone uses requests, even though the standard library has urllib.) How many of the new ideas in trio can be added to asyncio? No-one knows. I expect that there will be a lot of interesting discussions about this at PyCon this year :-).

Mf answered 26/3, 2018 at 6:58 Comment(13)
Thanks a lot. Sorry for late reply as it takes me some time to read your answer and references. Now I think I have understood trio much more, it does have advantages on design principle. In the past, I spent my spare time to understand the API design principle of requests, that helps me a lot on designing APIs related to network communication. Although trio is new-born now, learning it could improve my design on async IO. Thanks again.Ole
Another advantage of Trio: when you need to convert a synchronous library to Trio, often it's a simple job of sprinkling async/await everywhere you need it until the parts that require async context have it. If things are more complex, add a nursery and a couple of tasks. In my experience, Trio's concepts are easy enough to understand, and debugging Trio code is much easier than wrapping your head around asyncio.Carreno
"You have futures, tasks, and coroutines..." Although I'm using asyncio for more than a year now, this zoo of similar concepts still freaks me out.Overbold
Almost one year since this great answer (+1) - are there any major updates to make me start learning trio as opposed to the built-in asyncio?Carbo
@Carbo Trio has continued to mature, but as of Feb 2019 I think the answer is still basically accurate – Trio has a lot of theoretical advantages, but is still in the "early adopter" phase. This recent post on the forum might give you a sense of what we're working on. And for those looking for updates in the future, asking on the forum or chat is a quick way to get an update.Mf
asyncio implementation can be short too. gist.github.com/Ginkooo/d047178fb3a27349ecab39047bc9e126 Mocked network stuff, kinda ugly, but can be improvedSihon
@Sihon Unfortunately that's not really an implementation of Happy Eyeballs. You don't cancel other tasks when one completes successfully; you don't handle the parent task being cancelled; you don't expedite the next connection if the previous one finishes early... there's a happy eyeballs in asyncio here, and you'll see it's way more complicated than that: github.com/twisteroidambassador/async_stagger Or just watch the talk, it walks through the details :-)Mf
Nowadays there is also anyio: "AnyIO is an asynchronous networking and concurrency library that works on top of either asyncio or trio. Applications and libraries written against AnyIO’s API will run unmodified on either asyncio or trio." pypi.org/project/anyioAlburg
while the Framework :: Trio classifier only has 8. As of Oct 2021, it grew to 117 in the meantime. In terms of percentage it grew from 6.56% of asyncio's output to 11.91% (the latter also didn't skip the beat as it grew from 122 to 982 :) ). Also Twisted result is: 323.Eat
S-tier writeup. I went in skeptical, because the xkcd standards effect is real. I went away with an almost fanatical devotion to slowly (but inexorably) pivoting from asyncio into Trio in newer projects. Knowledge is bliss. Now I have it.Haggai
@Nathaniel J. Smith : How do you think trio stands in maturity in 2022?Rayford
Incredible writeup. Is the part that says For various reasons, I don't think this is fixable in asyncio no longer true now that AnyIO exists?Isomorphism
Another notable difference is the strategy for integrating a foreign event loop, such as Qt, Gtk, Tkinter, etc. See the Trio way and the Asyncio way if anyone is interested. Personally I prefer the former.Subsellium

© 2022 - 2024 — McMap. All rights reserved.