AsyncIO uses an event loop to pause and resume methods. For example, you can implement a simple corotine like this import types @types.coroutine def wait(seconds: int) -> None: start_time = time.time() while time.time() - start_time < seconds: yield This method will wait at least n seconds. But it is of course possible that it waits more than n seconds, since it will only continue when it is resumed, which will be on the next loop iteration. You could do a similar thing if you wanted to create a request and continue once it was successful. import socket from socket import BlockingIOError @types.coroutine def request(url: str) -> None: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((url, 80)) s.send(f'GET / HTTP/1.1\nHost: {url}\n\n'.encode()) s.setblocking(False) response = b'' while True: try: data = s.recv(1024) response += data except BlockingIOError: if response: break yield This method will check each iteration to see if data can be retrieved, and if so, continue with the other tasks in the event loop. I know that most modern operating systems provide a functionality to select all sockets that are ready. For example in the case of Linux this is epoll (which has a time complexity of O(1)). My question now is how to use this with AsyncIO. It would be great to have the functionality to resume execution of the method (generator) only when the socket is really ready. I have seen that asyncio exposes some socket methods like socket_connect. But I wasn't been able to find out if this uses selectors under the hood. @types.coroutine async def request(url: str) -> None: loop = asyncio.get_event_loop() await loop.sock_connect(url, 80) ... Continue reading...