简体   繁体   English

网址的Python Tornado异步获取

[英]Python Tornado Async Fetching of URLs

In the following code example I have a function do_async_thing which appears to return a Future , even though I'm not sure why? 在以下代码示例中,我有一个函数do_async_thing ,该函数似乎返回Future ,即使我不确定为什么?

import tornado.ioloop
import tornado.web
import tornado.httpclient

@tornado.gen.coroutine
def do_async_thing():
    http = tornado.httpclient.AsyncHTTPClient()
    response = yield http.fetch("http://www.google.com/")
    return response.body

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        x = do_async_thing()
        print(x) # <tornado.concurrent.Future object at 0x10753a6a0>

        self.set_header("Content-Type", "application/json")
        self.write('{"foo":"bar"}')
        self.finish()

if __name__ == "__main__":
    app = tornado.web.Application([
        (r"/foo/?", MainHandler),
    ])
    app.listen(8888)

    tornado.ioloop.IOLoop.current().start()

You'll see that I yield the call to fetch and in doing so I should have forced the value to be realised (and subsequently been able to access the body field of the response). 您会看到我yield了对fetch的调用,并且这样做我应该强制实现该值(随后便能够访问响应的body字段)。

What's more interesting is how I can even access the body field on a Future and not have it error (as far as I know a Future has no such field/property/method) 更有意思的是,我什至可以访问Future上的body字段,而不会出现错误(据我所知Future没有此类字段/属性/方法)

So does anyone know how I can: 有谁知道我该怎么做:

  1. Resolve the Future so I get the actual value 解决未来,让我得到实际价值
  2. Modify this example so the function do_async_thing makes multiple async url fetches 修改此示例,以便函数do_async_thing进行多个异步URL提取

Now it's worth noting that because I was still getting a Future back I thought I would try adding a yield to prefix the call to do_async_thing() (eg x = yield do_async_thing() ) but that gave me back the following error: 现在值得注意的是,因为我仍在获得Future,所以我想我会尝试添加一个yielddo_async_thing()的调用作为前缀(例如x = yield do_async_thing() ),但是这给了我以下错误:

tornado.gen.BadYieldError: yielded unknown object <generator object get at 0x1023bc308>

I also looked at doing something like this for the second point: 我还考虑了第二点:

def do_another_async_thing():
    http = tornado.httpclient.AsyncHTTPClient()
    a = http.fetch("http://www.google.com/")
    b = http.fetch("http://www.github.com/")
    return a, b

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        y = do_another_async_thing()
        print(y)

But again this returns: 但这又返回了:

<tornado.concurrent.Future object at 0x102b966d8>

Where as I would've expected a tuple of Futures at least? 正如我所期望的那样,至少在哪里有期货的元组? At this point I'm unable to resolve these Futures without getting an error such as: 在这一点上,我无法解决这些期货,而不会出现以下错误:

tornado.gen.BadYieldError: yielded unknown object <generator object get at 0x1091ac360>

Update 更新资料

Below is an example that works (as per answered by A. Jesse Jiryu Davis) 下面是一个有效的示例(根据A. Jesse Jiryu Davis的回答)

But I've also added another example where by I have a new function do_another_async_thing which makes two async HTTP requests (but evaluating their values are a little bit more involved as you'll see): 但是,我还添加了另一个示例,其中有一个新函数do_another_async_thing ,该函数发出两个异步HTTP请求(但是,如您所见,评估它们的值会涉及更多点):

def do_another_async_thing():
    http = tornado.httpclient.AsyncHTTPClient()
    a = http.fetch("http://www.google.com/")
    b = http.fetch("http://www.github.com/")
    return a, b

@tornado.gen.coroutine
def do_async_thing():
    http = tornado.httpclient.AsyncHTTPClient()
    response = yield http.fetch("http://www.google.com/")
    return response.body

class MainHandler(tornado.web.RequestHandler):
    @tornado.gen.coroutine
    def get(self):
        x = yield do_async_thing()
        print(x) # displays HTML response

        fa, fb = do_another_async_thing()
        fa = yield fa
        fb = yield fb
        print(fa.body, fb.body) # displays HTML response for each

It's worth clarifying: you might expect the two yield statements for do_another_async_thing to cause a blockage. 值得澄清:您可能希望do_another_async_thing的两个yield语句导致阻塞。 But here is a breakdown of the steps that are happening: 但是,这是正在发生的步骤的分解:

  • do_another_async_thing returns immediately a tuple with two Futures do_another_async_thing立即返回带有两个Future的元组
  • we yield the first tuple which causes the program to be blocked until the value is realised 我们yield第一个元组,它导致程序被阻塞,直到实现该值为止
  • the value is realised and so we move to the next line 该值已实现,因此我们移至下一行
  • we yield again, causing the program to block until the value is realised 我们再次yield ,导致程序阻塞,直到实现该值
  • but as both futures were created at the same time and run concurrently the second yield returns practically instantly 但是由于两个期货是同时创建并同时运行的,因此第二yield收益几乎立即返回

Coroutines return futures. 协程返回期货。 To wait for the coroutine to complete, the caller must also be a coroutine, and must yield the future. 为了等待协程完成,调用方必须是协程,并且必须产生未来。 So: 所以:

@gen.coroutine
def get(self):
    x = yield do_async_thing()

For more info see Refactoring Tornado Coroutines . 有关更多信息,请参见重构龙卷风协程

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM