我有一个非异步函数,如下所示:
def do_stuff(on_finished):
result = # complicated calculations here
on_finished(result)
我传入的回调或多或少是这样的:
async def on_finished(self, result):
response = await post_over_http(result)
self.last_status = response.status
当我打电话给do_stuff
时,我想发生的事情是这样的:
do_stuff
执行并调用on_finished
on_finished
执行,通过HTTP发布结果,然后立即返回。do_stuff
现在立即返回。on_finished
的第二行。至关重要的是,我不希望do_stuff异步。出于架构上的原因,我希望do_stuff
与网络代码的异步性质隔离,所以我不想仅由于使用它的某些代码是异步的而使其异步。
在JavaScript中这没问题-基本上将上面的代码直接转录为JavaScript,我将获得所需的行为。 onFinished
将返回一个承诺,doStuff
不会等待并立即返回,但是当该承诺稍后解析时,onFinished
的第二行就会运行。这在Python中可行吗?我不确定如何实现。使用以上代码,我想我只是在do_stuff
的最后一行中创建了一个协程,但从未调用它。
答案 0 :(得分:1)
您可以这样设计do_stuff
函数:
def do_stuff(on_finished):
async def _do_complicated_calculation():
result = # do the calculation here & the post request
await on_finished(result)
asyncio.ensure_future(_do_complicated_calculation())
return "ok"
当您调用do_stuff(...)
时,复杂的计算将添加到asyncio事件循环中,因此它将异步执行。如果您不打算在主线程中启动事件循环,则应在另一个线程中运行该事件循环。
由于_do_complicated_calculation()
是异步的,因此do_stuff将首先返回"ok"
,并且在计算完成之后,将调用on_finished(...)
。
答案 1 :(得分:0)
这就是为什么Javascript与Python行为不同的原因。
ECMAScript 2015引入了作业队列的概念, 由Promises(也在ES6 / ES2015中引入)。这是执行 异步功能的结果,而不是被放置 在调用堆栈的末尾。
在当前功能结束之前解决的承诺将是 在当前函数之后立即执行。
答案 2 :(得分:0)
同步函数不传递回调,而是返回其结果,并使用run_in_executor
进行从异步代码到将在其自己的线程上运行的同步代码的调用。
def do_stuff():
result = # complicated calculations here
return result
async def main():
loop = asyncio.get_event_loop()
result = await loop.run_in_executor(None, do_stuff)
response = await post_over_http(result)
self.last_status = response.status
未经测试。
答案 3 :(得分:-1)
如果我了解JavaScript的类比,那么您想要的是这样的:
def do_stuff(on_finished):
result = ...
asyncio.create_task(on_finished(result))
最后一行产生一个处理结果的任务,而无需实际等待它完成。这是您在JavaScript中通过简单地创建一个Promise就能得到的,而在Python中,您必须更加明确。
当然,do_stuff
必须在事件循环内运行,并且计算不得阻塞(或花费太长时间才能完成),但JavaScript就是这种情况。