I have a synchronous iterator, that comes from third party package. The iterator queries external service and yields some data. If there is no data, the iterator waits for it. I subclassed the WebSocketEndpoint
from Starlette to send the new data from iterator through websocket. Unfortunately, it seems like I don't understand something and my code doesn't work as expected. Here is a slightly simplified code:
import time
from starlette.endpoints import WebSocketEndpoint
from starlette.websockets import WebSocket
class Iterator:
"""This is a third-party object, not asynchronous at all."""
def __init__(self):
self._stop = False
def __iter__(self):
self.i = 0
return self
def __next__(self):
if self._stop:
raise StopIteration
time.sleep(5)
self.i += 1
print(self.i)
return self.i
def cancel(self):
self._stop = True
class MyWebSocket(WebSocketEndpoint):
def __init__(self, scope, receive, send) -> None:
super().__init__(scope, receive, send)
self.iterator = Iterator()
async def on_connect(self, websocket: WebSocket) -> None:
await super().on_connect(websocket)
for message in self.iterator:
await websocket.send_json({"message": message})
async def on_disconnect(self, websocket: WebSocket, close_code: int) -> None:
await super().on_disconnect(websocket, close_code)
self.iterator.cancel()
First problem - the code doesn't send any data through websocket. The print statement indicates, that the iterator produces data, but nothing is actually sent. If I'll put return
after websocket.send_json()
, it will send the first result from the iterator correctly, but the loop will finish afterward. Why?
Another problem is that the iterator completely blocks application execution. I understand why it happens, but since it is a web service and the iterator is meant to work until the client disconnects from the websocket it can easily block my whole application. If I'll have 10 workers, 10 websocket clients will block the application and it won't be possible to do anything until one of the websockets will disconnect. How can I resolve it?