One WebSocket carries everything. A command goes out with an id; Chrome's reply comes back with the same id, so _process_single_message resolves that command's Future inline. Events have no id: they drop onto a separate _event_queue and a worker drains them in order to your callbacks. The reply lane and the event lane never block each other.
pydoll
ConnectionHandler
_id → 1
ws.send()  →  ←  ws.recv()
_process_single_message
_is_command_response → "id" in msg ?
Chrome
--remote-debugging-port
runs cmds · emits events
_pending_commandsdict[int, Future]
_event_queueasyncio.Queue · FIFOqueued: 0
events drop in here
_process_event_queue · idle
on(Network.requestWillBeSent)0
on(Page.loadEventFired)0
network_logs · deque(maxlen=10000): 0
command reply (by id) event (no id) one WebSocket · id-correlated replies resolve inline · events flow through the queue