I was going through following code in the asyncio doc.
import asyncio
async def tcp_echo_client(message):
reader, writer = await asyncio.open_connection(
'127.0.0.1', 8888)
print(f'Send: {message!r}')
writer.write(message.encode())
data = await reader.read(100)
print(f'Received: {data.decode()!r}')
print('Close the connection')
writer.close()
await writer.wait_closed()
asyncio.run(tcp_echo_client('Hello World!'))
However I am now able to understand why reader.read is awaitable but writer.write is not ? Since they are both I/O operations write method should also be awaitable right ?
Not necessarily. The fundamental asymmetry between
read()
andwrite()
is thatread()
must return actual data, whilewrite()
operates purely by side effect. Soread()
must be awaitable because it needs to suspend the calling coroutine when the data isn't yet available. On the other hand,write()
can be (and in asyncio is) implemented by stashing the data in some buffer and scheduling it to be written at an opportune time.This design has important consequences, such as that writing data faster than the other side reads it causes the buffer to bloat up without bounds, and that exceptions during
write()
are effectively lost. Both issues are fixed by callingwriter.drain()
which applies backpressure, i.e. writes out the buffer to the OS, if necessary suspending the coroutine in the process. This is done until the buffer size drops beneath a threshold. Thewrite()
documentation advises that "calls towrite()
should be followed bydrain()
."The lack of backpressure in
write()
is a result of asyncio streams being implemented on top of a callback-based layer in which a non-asyncwrite()
is much more convenient to use than a fully asynchronous alternative. See this article by Nathaniel J Smith, the author of trio, for a detailed treatment of the topic.