Understanding asyncio: Asynchronous vs synchronous callbacks
<p>There's one very particular thing about <code>asyncio</code> that I can't seem to understand. And that is the difference between asynchronous and synchronous callbacks. Let me introduce some examples.</p>

<p>Asyncio TCP example:</p>

<pre><code>class EchoServer(asyncio.Protocol):

def connection_made(self, transport):
    print('connection made')

def data_received(self, data):
    print('data received: ', data.decode())

def eof_received(self):
    pass

def connection_lost(self, exc):
    print('connection lost:', exc)
</code></pre>

<p>Aiohttp example:</p>

<pre><code>async def simple(request):
    return Response(text="Simple answer")

async def init(loop):
    app = Application(loop=loop)
    app.router.add_get('/simple', simple)
    return app

loop = asyncio.get_event_loop()
app = loop.run_until_complete(init(loop))
run_app(app, loop=loop)
</code></pre>

<p>Those two examples are very similar in functionality but they seem to be both doing it in different way. In the first example, if you want to be notified on some action, you specify a synchronous function (<code>EchoServer.connection_made</code>). However, in the second example, if you want to be notified on some action, you have to define an asynchronous callback function (<code>simple</code>).</p>

<p>I would like to ask what is the difference between these two types of callback. I understand the difference between regular functions and asynchronous functions, but I cannot wrap my head around the callback difference. For example, if I would want to write an asynchrnonous API like <code>aiohttp</code> is, and I would have a function that would do something and call a callback after that, how would I decide if I should demand an asynchronous function to be passed as an argument or just a regular synchronous one?</p>

<p>In <code>aiohttp</code> example you could do asynchronous calls from <code>simple</code> web-handler: access to database, make http requests etc.</p>

<p>In <code>Protocol.data_received()</code> you should call only regular synchronous methods.</p>

<p><strong>UPD</strong></p>

<p><code>Protocol</code> callbacks are supposed to be synchronous by design.
They are very low-level bridge between sync and async. 
You may call async code from them but it requires really tricky coding.
User level <code>asyncio</code> API for sockets etc. are streams: <a href="https://docs.python.org/3/library/asyncio-stream.html" rel="nofollow">https://docs.python.org/3/library/asyncio-stream.html</a></p>

<p>When you introduce your own callback system must likely you need asynchronous callback unless you are %100 sure why the callback will never want to call async code.</p>

<p>Regular functions (<code>def</code>) and coroutines (<code>async def</code>) have a different signatures. It's hard to change a required signature, especially if your code has published as a library and you cannot control all users of your callback.</p>

<p>P.S.</p>

<p>The same is true for any public API methods.</p>

<p>The hardest lesson I've learned during development of my libraries is: <code>.close()</code> method <strong>should</strong> be a coroutine even initially it calls sync functions only, e.g. <code>socket.close()</code>.</p>

<p>But later you perhaps will want to add a graceful shutdown which requires a waiting for current activity finishing and so on.</p>

<p>If your users had call your api as <code>obj.close()</code> now they should use <code>await obj.close()</code> but it's <strong>backward incompatible</strong> change!</p>

python asynchronous callback python-asyncio