使用 await 或 yield 迭代循环会导致错误

Iterating a loop using await or yield causes error

我来自 Twisted/Klein. I come in peace and to ask for Tornado help. I'm investigating Tornado and how its take on async differs from Twisted. Twisted has something similar to gen.coroutine which is defer.inlineCallbacks 的土地,我能够编写这样的异步代码:

kleinsample.py

@app.route('/endpoint/<int:n>')
@defer.inlineCallbacks
def myRoute(request, n):
    jsonlist = []
    for i in range(n):
        yield jsonlist.append({'id': i})
    return json.dumps(jsonlist)

卷曲命令:

curl localhost:9000/json/2000

此端点将创建一个包含 n 个元素的 JSON 字符串。 n 可以很小也可以很大。我可以在 Twisted 中将其分解,这样事件循环就不会使用 yield 阻塞。下面是我尝试将其转换为 Tornado 的方法:

tornadosample.py

async def get(self, n):
    jsonlist = []
    for i in range(n):
        await gen.Task(jsonlist.append, {'id': i})    # exception here
    self.write(json.dumps(jsonlist))

回溯:

 TypeError: append() takes no keyword arguments

我对应该如何正确迭代循环中的每个元素以使事件循环不被阻塞感到困惑。有谁知道"Tornado"这样做的方法吗?

我们来看看gen.Task docs:

Adapts a callback-based asynchronous function for use in coroutines.

Takes a function (and optional additional arguments) and runs it with those arguments plus a callback keyword argument. The argument passed to the callback is returned as the result of the yield expression.

由于 append 不接受关键字参数,因此它不知道如何处理该 callback kwarg 并吐出该异常。

您可以做的是用您自己的函数包装 append,该函数确实接受 callback kwarg 或 this 答案中显示的方法。

您不能也不能等待 append,因为它不是协程,也不是 return 未来。如果您想偶尔屈服以允许其他协同程序继续使用 Tornado 的事件循环,请等待 gen.moment.

from tornado import gen

async def get(self, n):
    jsonlist = []
    for i in range(n):
        jsonlist.append({'id': i})
        if not i % 1000:  # Yield control for a moment every 1k ops
           await gen.moment
    return json.dumps(jsonlist)

就是说,除非此功能 非常 CPU 密集并且需要数百毫秒或更长时间才能完成,否则您最好只做所有的事情一次计算,而不是在函数 returns.

之前多次通过事件循环

list.append() returns None,所以你的克莱因样本看起来像是在产生一些物体,这有点误导人。这相当于 jsonlist.append(...); yield 作为两个单独的语句。龙卷风等效项是 await gen.moment 代替裸 yield

另请注意,在 Tornado 中,处理程序通过调用 self.write() 而不是通过返回值来产生响应,因此 return 语句应为 self.write(json.dumps(jsonlist)).