可选的异步函数同步接口

时间:2016-09-29 19:54:51

标签: python asynchronous tornado synchronous

我正在编写一个使用Tornado Web tornado.httpclient.AsyncHTTPClient创建请求的库,该代码为我的代码提供了async接口:

async def my_library_function():
    return await ...

如果用户提供了一个kwarg,我希望这个界面可选地是串行的 - 例如:serial=True。虽然在没有async的情况下,您无法使用普通函数调用使用await关键字定义的函数。这将是理想的 - 虽然目前在语言中几乎肯定是不可能的:

async def here_we_go():
    result = await my_library_function()
    result = my_library_function(serial=True)

我无法在网上找到任何人提出一个很好的解决方案。我不想在没有awaits遍布的情况下重新实现基本相同的代码。

这是可以解决的问题,还是需要语言支持?

解决方案(虽然使用Jesse代替 - 下面解释)

以下Jesse的解决方案几乎就是我将要采用的解决方案。我最终通过使用装饰器获得了我最初想要的界面。像这样:

import asyncio
from functools import wraps


def serializable(f):
    @wraps(f)
    def wrapper(*args, asynchronous=False, **kwargs):
        if asynchronous:
            return f(*args, **kwargs)
        else:
            # Get pythons current execution thread and use that
            loop = asyncio.get_event_loop()
            return loop.run_until_complete(f(*args, **kwargs))
    return wrapper

这为您提供了这个界面:

result = await my_library_function(asynchronous=True)
result = my_library_function(asynchronous=False)

我在python的异步邮件列表中检查了这一点,我很幸运能让Guido做出回应,并且因为这个原因他礼貌地将其击落:

  

代码气味 - 能够异步调用相同的功能   同步是非常令人惊讶的。它也违反了规则   拇指,参数的值不应该影响返回类型。

很高兴知道它有可能虽然不被认为是一个很棒的界面。 Guido基本上建议了Jesse的答案,并将包装函数作为库中的辅助函数引入,而不是将其隐藏在装饰器中。

1 个答案:

答案 0 :(得分:5)

如果要同步调用此类函数,请使用run_until_complete

asyncio.get_event_loop().run_until_complete(here_we_go())

当然,如果您经常在代码中执行此操作,则应该为此语句提供缩写,可能只是:

def sync(fn, *args, **kwargs):
    return asyncio.get_event_loop().run_until_complete(fn(*args, **kwargs))

然后你可以这样做:

result = sync(here_we_go)