Issue
I have not worked much with async python, but currently i have a project with Sanic framework. There is websocket endpoint, which receives data from client, sends message to client, that task have been started, then runs long sync task (there is no options to make it async) and finally sends message to client that task is done. There is some example code:
from sanic import Sanic
from sanic.server.protocols.websocket_protocol import WebSocketProtocol
app = Sanic("TestApp")
@app.websocket("/")
async def process_task(request, ws: WebSocketServerProtocol):
raw_data = await ws.recv()
data = json.loads(raw_data)
await ws.send("TASK STARTED")
process_long_task(data) # Long sync function
await ws.send("TASK ENDED")
But there is a problem. Function does not wait for ws.send("TASK STARTED"). Actually both messages are send only after process_long_task finishes. Although, it's working correctly if i add await asyncio.sleep(0.1) after await ws.send("TASK STARTED")
Can someone point out to me what's wrong with my code?
Solution
If you call a synchronous function from a coroutine the entire loop and all tasks running inside it will be waiting for that single synchronous function to complete.
If you need to have a long running synchronous function, and await asyncronously for the result there are a few ways, most people use a background thread of some kind.
Here is an example of socket.gethostbyaddr()
(a sometimes very slow to finish synchronous function) being used asyncronously.
import socket
import concurrent.futures
import asyncio
async def GetHostFromAddr(ip):
"""
GetHostFromAddr(ip) -> fqdn\n
:param ip: The hosts ip address ie. 192.168.1.1
:return: Return the fqdn (a string of the form 'sub.example.com') for a host.
"""
loop = asyncio.get_event_loop()
pool = concurrent.futures.ThreadPoolExecutor()
return await loop.run_in_executor(pool, socket.gethostbyaddr, ip)
Answered By - 0xKate
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.