In asyncio and similar systems, the choice between "is this code syncronous or asyncronous" is made at declaration time - either you define a sync function or an async one, and you can only use it in that way.
In gevent and similar systems, the same choice is made at call time - there's no distinction between sync and async functions, instead you can call syncronously with "foo()" or asyncronously with "future = gevent.spawn(foo)" (I'm taking some liberties by calling the returned greenlet object a future but it can be used as such).