![](/img/trans.png)
[英]Awaiting a coroutine and assigning the result to a variable outside of a coroutine
[英]How to run a coroutine that does not end without getting stuck awaiting a result?
我有這段代碼,我想連接到websocket並通過發送心跳保持連接活着。 在這樣做時,我也希望能夠將有效負載發送到websocket,但是我一直卡住等待keepAlive
協程的結果
import asyncio
import websockets
ws=#path goes here
async def sendHeartbeat(socket):
message={
"op": 1,
"d": 0
}
await socket.send(json.dumps(message))
print("heartbeat sent")
async def keepAlive(socket):
while 1==1:
await sendHeartbeat(socket)
resp=await(socket.recv())
print(resp)
if json.loads(resp)["op"]==11:
#the server sent an ack back
print("ack received")
else:
socket.close()
await asyncio.sleep(10)
async def beginSocket(loop):
async with websockets.connect(ws) as socket:
print(await socket.recv())
await keepAlive(socket)
#payloads would be sent here
print("Hey payloads being sent and stuff")
loop = asyncio.get_event_loop()
loop.run_until_complete(beginSocket(loop))
但是,使用這段代碼,等待keepAlive
之后的print語句永遠不會打印出來。 我怎樣才能使代碼不等待keepAlive
的結果?
雖然keepAlive(socket)
會立即返回,但由於keepAlive
是一個couroutine,因此await keepAlive(socket)
永遠不會返回,因為keepAlive()
包含一個無限循環。
而不是使用await
,嘗試asyncio.ensure_future(keepAlive(socket))
。
如果您確實想使用await keepAlive(socket)
,請嘗試從其他地方發送有效負載(可能事先使用asyncio.ensure_future(send_payload(socket))
)。
在這種情況下,您在概念上通過同一套接字進行兩次單獨的對話。 一個對話就是你的心跳和回復信息。 另一個是您要發送的其他數據包。
我會保留3個獨立的頂級(即他們直接向事件循環報告)任務。 我希望他們都能引用一些協調員,所以當有時間時,他們中的任何一個都可以取消所有其他協調員。
keepAlive
功能。 多路復用任務的工作是將消息路由到適當的任務。 心跳任務應該只獲得心跳響應,而另一個任務應該獲取所有其他消息。
由於websockets已經對消息進行了框架處理,因此您只能send
或recv
整個消息,因此它可能具有的其他作業無關緊要。
這是你可以寫這個的一種方式。
import asyncio
import websockets
ws=#path goes here
class RoutingTask(object):
def __init__(self, sock, defaultQueue, **kwargs):
super().__init__(**kwargs)
self.sock = sock
self.defaultQueue = defaultQueue # The queue all messages not otherwise matched go to.
self.matchers = []
async def run(self):
while True:
msg = await self.sock.recv()
msg = json.loads(msg)
matched = False
for matcher, queue in matchers:
if matcher(msg):
await queue.put(msg)
matched = True
break
if not matched:
await self.defaultQueue.put(msg)
def addMatcher(self, matcher, queue):
el = (matcher, queue)
self.matchers.append(el)
async def heartbeatTask(wssock, incomingq):
message=json.dumps({
"op": 1,
"d": 0
}) # Do this just once.
while True:
await wssock.send(message)
print("heartbeat sent")
response = await asyncio.wait_for(incomingq.get(), 10) # Wait 10 seconds for response.
assert response['op'] == 11
print("heartbeat response received.")
await asyncio.sleep(10) # Wait 10 seconds to send another heartbeat.
async def beginSocket(loop):
def heartbeatMatcher(jsondict):
return jsondict.get('op', None) == 11
async with websockets.connect(ws) as socket:
myq = asyncio.Queue(maxsize=1)
heartbeatq = asyncio.Queue(maxsize=1)
router = RoutingTask(socket, myq)
router.addMatcher(heartbeatMatcher, heartbeatq)
router = asyncio.ensure_future(router.run())
heartbeat = asyncio.ensure_future(heartbeatTask(socket, heartbeatq)
print(await myq.get())
#payloads would be sent here
print("Hey payloads being sent and stuff")
heartbeat.cancel() # Stop the heartbeat
router.cancel() # Stop the router task
loop = asyncio.get_event_loop()
loop.run_until_complete(beginSocket(loop))
這里有一些問題。 如果拋出異常, heartbeat
和router
任務可能不會被取消。 他們也沒有很好的方法將問題報告回主beginSocket
任務。 這基本上是一種快速和骯臟的一次性,以演示如何做你想做的事情。
在我看來, asyncio.ensure_future
被錯誤命名。 它的作用是告訴事件循環它需要保持運行的新事物。 它基本上是啟動了一個線程的協同程序。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.