Imagine several cash register clerks and a single bagger in the checkout aisles of a grocery store. Let's assume that the clerks are very slow at scanning foods. How might a fast bagger deal with this?

A synchronous bagger would be required to stay put in one aisle and bag the food as it slowly gets scanned. When food isn't scanned yet, he'll spend his free time scrolling news feeds on his phone. When all the food is finally scanned and bagged, he'll move over to the next busy aisle and start bagging there.

A synchronous bagger finishes bagging at Aisle 1 by time 9 and at Aisle 2 by time 13. He is idle during 5 of those time units.

An asynchronous bagger would bag all food that is already scanned, transfer to another aisle and bag scanned food there, and then continue round-robin through busy aisles until he eventually returns to his starting aisle. There, he'll bag any new food that has been scanned since he left. This routine is repeated until all food is scanned and bagged.

An asynchronous bagger finishes bagging at Aisle 1 by time 9 and at Aisle 2 by time 10. He is idle during 2 of those time units.

In programmer-speak, this bagger is I/O-bound because his work pace is determined by the rate at which food is scanned, not the rate at which he can bag the incoming food. Such I/O-bound applications use non-blocking calls to accomplish more work with their otherwise idle CPU cycles. This is the key difference between the synchronous bagger (required to stay in his aisle and blocked from bagging until more food is scanned) and the asynchronous bagger (free to roam to other aisles and bag there while waiting).

The asynchronous bagger seems more efficient. Is he actually faster?

Well, bagging can be completed faster in the asynchronous case. This isn't because the bagger is working faster, but because he's doing work in other aisles instead of wasting time on his phone. Said another way, the bagging rate is the same for both the synchronous and asynchronous cases, but the asynchronous bagger has less idle time.

However, this isn't the full picture. Bagging can also be completed slower in the asynchronous case. There's overhead in switching aisles that might slow the bagger down. And even when the overall bagging is completed faster, it's still possible for any individual shopper to experience a delay in how long it takes to have all their food bagged. A bagger might leave an aisle partway through the scanning of that shopper's food and only return after he's visited 20 other aisles while the shopper waits to have his remaining food bagged.

This asynchronous bagger has overhead when switching aisles. Compared to the synchronous case, bagging at Aisle 1 takes longer while Aisle 2 is completed in the same amount of time.

Maybe it'd be more efficient to have several synchronous baggers working in parallel instead of one asynchronous bagger. In Python, benchmarks show that, when the number of shoppers increases significantly (and the number of open aisles increases), asynchronous workers just don't handle the load as well as threaded workers or parallelized synchronous ones. Of course, threading comes with its own set of potential problems, including deadlocks, starvation, and race conditions.

In the end, it'll depend on your particular situation to determine which mode performs best for you.