miguelgrinberg / python-socketio

Python Socket.IO server and client
MIT License
3.96k stars 587 forks source link

Async Handlers for Client Handlers when the Emit Originates from the Server #1054

Closed frankgreco closed 9 months ago

frankgreco commented 2 years ago

Alright so what I'm seeing is the following 👇🏼

Two "emits" from socket (JS if that matters) where the second emit occurs before the first one finishes. I'm seeing synchronization of the events rather than them occur concurrently.

Received event "<event_name>" [/]
# logs saying my long running custom handler is starting
# ---> server emits second event to client somewhere here
# logs saying my long running custom handler is finishing
# ---> but it's not received until the previous event is done
Received event "<event_name>" [/]
# logs saying my long running custom handler is starting/finishing

Now there's a lot of issues and stack overflow posts where people are running into this when the emits originate from the client and the solution is to set async_handlers=True on the server. However, the emit is originating in the server to a client handler so I don't have that tunable.

I know this works when I have the client implemented in JS and i'm basically cloning that client in Python and can't figure this one specific thing out.

I can send more logs if needed. Here's a snipped of my handler👇🏼

def register(self, event, plugin):
    self._socket.on(event, handler=self.__with_closure(plugin))

def __with_closure(self, plugin):
    async def run(event, metadata, request, timings):

        param1, param2 = await long_running_handler()

        return (param1, param2)

    return run
miguelgrinberg commented 2 years ago

This is incorrect, the client does not have an async_handlers flag because it always dispatches events asynchronously.

Example server-side code:

def send_tests(sid):
    sio.sleep(3)
    sio.emit('test', 'first call', to=sid)
    sio.emit('test', 'second call', to=sid)
    sio.emit('test', 'third call', to=sid)

On the client:

@sio.event
def connect():
    print('connected to server')

@sio.event
def test(data):
    print('start with', data)
    for i in range(3):
        print('running with', data, f'{i+1}/3')
        sio.sleep(1)
    print('end with', data)

Logs:

❯ python client.py
connected to server
start with first call
running with first call 1/3
start with second call
running with second call 1/3
start with third call
running with third call 1/3
running with first call 2/3
running with second call 2/3
running with third call 2/3
running with first call 3/3
running with second call 3/3
running with third call 3/3
end with first call
end with second call
end with third call