如何在 Python 3 asyncio 中实现同步等待回调?

How do I implement synchronously waiting for a callback in Python 3 asyncio?

由于一些不寻常的限制,我需要在返回响应之前同步等待来自另一个服务的回调 URL。目前我有类似的东西:

ROUTE = '/operation'
async def post(self):
    ##SOME OPERATIONS##
    post_body = { 'callbackUrl' : 'myservice.com/cb' }
    response = await other_service.post('/endpoint')
    global my_return_value
    my_return_value = None
    while not my_return_value:
        pass
    return self.make_response(my_return_value)

然后我有办法处理回调 URL 类似于:

ROUTE = '/cb'
async def post(self):
    ##OPERATIONS###
    global my_return_value
    my_return_value = some_value
    return web.json_response()

此代码的问题在于它永远陷入 while 循环,即使回调 URL 被调用也是如此。我怀疑有更好的方法可以做到这一点,但我不确定如何去做,也不知道如何 google 。有什么想法吗?

提前致谢!

只是快速浏览一下,但我认为你被困在了

while not my_return_value:
    pass

Python会被困在那里没时间处理回调函数。你需要的是

while not my_return_value:
    await asyncio.sleep(1)

(或者,如果您不想延迟毫秒,您甚至可以执行 asyncio.sleep(0))。

更好的方法是(现在我是凭记忆写的,不能保证...):

my_return_value = asyncio.get_event_loop().create_future()
await my_return_value
return self.make_response(my_return_value.result())


async def post(self):
    ##OPERATIONS###
    my_return_value.set_result(some_value)
    return web.json_response()

但是请注意,如果同时使用此系统的人数超过一个,那么任何一种方式都会破坏很多。感觉很脆弱!也许更好:

ROUTE = '/operation'
my_return_value = {}

async def post(self):
    ##SOME OPERATIONS##
    token = "%016x" % random.SystemRandom().randint(0, 2**128)
    post_body = { 'callbackUrl' : 'myservice.com/cb?token='+token }
    response = await other_service.post('/endpoint')
    my_return_value[token] = asyncio.get_event_loop().create_future()
    await my_return_value[token]
    result = my_return_value[token].result()
    del my_return_value[token]
    return self.make_response(result)

async def post(self):
    ##OPERATIONS###
    token = self.arguments("token")
    my_return_value[token].set_result(some_value)
    return web.json_response()

现在 cherry on top 将是一个计时器,它会在超时后取消 future 并在一段时间后如果回调没有发生则清理 my_return_value 中的条目。此外,如果您接受我最后的建议,请不要将其命名为 my_return_value,而应命名为 callback_future_by_token...