es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Python async-await no está esperando.

No he trabajado mucho con Python asíncrono, pero actualmente tengo un proyecto con el marco de trabajo Sanic. Hay un punto final de websocket que recibe datos del cliente, envía un mensaje al cliente indicando que se ha iniciado la tarea, luego ejecuta una tarea larga de sincronización (no hay opciones para hacerlo asíncrono) y finalmente envía un mensaje al cliente indicando que la tarea está terminada. Aquí hay un código de ejemplo:

from sanic import Sanic
from sanic.server.protocols.websocket_protocol import WebSocketProtocol

app = Sanic("TestApp")

@app.websocket("/")
async def procesar_tarea(request, ws: WebSocketServerProtocol):
    datos_crudos = await ws.recv()
    datos = json.loads(datos_crudos)
    await ws.send("TAREA INICIADA")
    procesar_tarea_larga(datos) # Función de sincronización larga
    await ws.send("TAREA FINALIZADA")

Pero hay un problema. La función no espera a ws.send("TAREA INICIADA"). De hecho, ambos mensajes se envían solo después de que procesar_tarea_larga termina. Sin embargo, funciona correctamente si agrego await asyncio.sleep(0.1) después de await ws.send("TAREA INICIADA").

¿Alguien puede señalarme qué está mal en mi código?

Tags:  , ,

Answer

  1. Avatar for davy.ai

    El problema con el código es que no permite que el bucle de eventos ejecute el método send() antes de ejecutar la función process_long_task(). Aunque el método send() es asíncrono, no se está esperando su finalización antes de ejecutar la tarea de sincronización larga. Para solucionar esto, puedes utilizar asyncio.create_task() para ejecutar la tarea de sincronización larga en segundo plano mientras permite que el bucle de eventos continúe ejecutando otras tareas, incluyendo el envío del mensaje al cliente.

    Aquí tienes un ejemplo actualizado:

    import asyncio
    import json
    from sanic import Sanic
    from sanic.server.protocols.websocket_protocol import WebSocketProtocol
    
    app = Sanic("TestApp")
    
    async def run_task_and_send_result(ws, data):
        await ws.send("TAREA INICIADA")
        await asyncio.sleep(0) # permite que el bucle de eventos envíe el mensaje antes de comenzar la tarea
        await asyncio.create_task(process_long_task(data))
        await ws.send("TAREA TERMINADA")
    
    def process_long_task(data):
        # función de sincronización larga
    
    @app.websocket("/")
    async def process_task(request, ws: WebSocketProtocol):
        raw_data = await ws.recv()
        data = json.loads(raw_data)
        await run_task_and_send_result(ws, data)
    

    En este código actualizado, el método send() se espera antes de llamar a create_task() para ejecutar la tarea de sincronización larga en segundo plano. Además, se utiliza asyncio.sleep(0) para permitir que el bucle de eventos ejecute el método send() antes de ejecutar la tarea. La tarea de sincronización larga se ejecuta utilizando create_task() para que se ejecute en segundo plano y no bloquee el bucle de eventos. Finalmente, se envía el segundo mensaje después de que la tarea esté completa.

Comments are closed.