🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
### Navigation - [index](# "General Index") - [modules](# "Python Module Index") | - [next](# "Queue example - a concurrent web spider") | - [previous](# "Asynchronous and non-Blocking I/O") | - [Tornado 4.4.dev1 documentation](#) » - [User's guide](#) » # Coroutines **Coroutines** are the recommended way to write asynchronous code inTornado. Coroutines use the Python `yield` keyword to suspend andresume execution instead of a chain of callbacks (cooperativelightweight threads as seen in frameworks like [gevent](http://www.gevent.org) [http://www.gevent.org] are sometimes called coroutines as well, butin Tornado all coroutines use explicit context switches and are calledas asynchronous functions). Coroutines are almost as simple as synchronous code, but without theexpense of a thread. They also [make concurrency easier](https://glyph.twistedmatrix.com/2014/02/unyielding.html) [https://glyph.twistedmatrix.com/2014/02/unyielding.html] to reasonabout by reducing the number of places where a context switch canhappen. Example: ~~~ from tornado import gen @gen.coroutine def fetch_coroutine(url): http_client = AsyncHTTPClient() response = yield http_client.fetch(url) # In Python versions prior to 3.3, returning a value from # a generator is not allowed and you must use # raise gen.Return(response.body) # instead. return response.body ~~~ ### Python 3.5: `async` and `await` Python 3.5 introduces the `async` and `await` keywords (functionsusing these keywords are also called “native coroutines”). Starting inTornado 4.3, you can use them in place of `yield`-based coroutines.Simply use `async def foo()` in place of a function definition withthe `@gen.coroutine` decorator, and `await` in place of yield. Therest of this document still uses the `yield` style for compatibilitywith older versions of Python, but `async` and `await` will runfaster when they are available: ~~~ async def fetch_coroutine(url): http_client = AsyncHTTPClient() response = await http_client.fetch(url) return response.body ~~~ The `await` keyword is less versatile than the `yield` keyword.For example, in a `yield`-based coroutine you can yield a list of`Futures`, while in a native coroutine you must wrap the list in[`tornado.gen.multi`](# "tornado.gen.multi"). You can also use [`tornado.gen.convert_yielded`](# "tornado.gen.convert_yielded")to convert anything that would work with `yield` into a form thatwill work with `await`. While native coroutines are not visibly tied to a particular framework(i.e. they do not use a decorator like [`tornado.gen.coroutine`](# "tornado.gen.coroutine") or[`asyncio.coroutine`](https://docs.python.org/3.4/library/asyncio-task.html#asyncio.coroutine "(in Python v3.4)") [https://docs.python.org/3.4/library/asyncio-task.html#asyncio.coroutine]), not all coroutines are compatible with eachother. There is a *coroutine runner* which is selected by the firstcoroutine to be called, and then shared by all coroutines which arecalled directly with `await`. The Tornado coroutine runner isdesigned to be versatile and accept awaitable objects from anyframework; other coroutine runners may be more limited (for example,the `asyncio` coroutine runner does not accept coroutines from otherframeworks). For this reason, it is recommended to use the Tornadocoroutine runner for any application which combines multipleframeworks. To call a coroutine using the Tornado runner from within acoroutine that is already using the asyncio runner, use the[`tornado.platform.asyncio.to_asyncio_future`](# "tornado.platform.asyncio.to_asyncio_future") adapter. ### How it works A function containing `yield` is a **generator**. All generatorsare asynchronous; when called they return a generator object insteadof running to completion. The `@gen.coroutine` decoratorcommunicates with the generator via the `yield` expressions, andwith the coroutine's caller by returning a [`Future`](# "tornado.concurrent.Future"). Here is a simplified version of the coroutine decorator's inner loop: ~~~ # Simplified inner loop of tornado.gen.Runner def run(self): # send(x) makes the current yield return x. # It returns when the next yield is reached future = self.gen.send(self.next) def callback(f): self.next = f.result() self.run() future.add_done_callback(callback) ~~~ The decorator receives a [`Future`](# "tornado.concurrent.Future") from the generator, waits (withoutblocking) for that [`Future`](# "tornado.concurrent.Future") to complete, then “unwraps” the [`Future`](# "tornado.concurrent.Future")and sends the result back into the generator as the result of the`yield` expression. Most asynchronous code never touches the [`Future`](# "tornado.concurrent.Future")class directly except to immediately pass the [`Future`](# "tornado.concurrent.Future") returned byan asynchronous function to a `yield` expression. ### How to call a coroutine Coroutines do not raise exceptions in the normal way: any exceptionthey raise will be trapped in the [`Future`](# "tornado.concurrent.Future") until it is yielded. Thismeans it is important to call coroutines in the right way, or you mayhave errors that go unnoticed: ~~~ @gen.coroutine def divide(x, y): return x / y def bad_call(): # This should raise a ZeroDivisionError, but it won't because # the coroutine is called incorrectly. divide(1, 0) ~~~ In nearly all cases, any function that calls a coroutine must be acoroutine itself, and use the `yield` keyword in the call. When youare overriding a method defined in a superclass, consult thedocumentation to see if coroutines are allowed (the documentationshould say that the method “may be a coroutine” or “may return a[`Future`](# "tornado.concurrent.Future")”): ~~~ @gen.coroutine def good_call(): # yield will unwrap the Future returned by divide() and raise # the exception. yield divide(1, 0) ~~~ Sometimes you may want to “fire and forget” a coroutine without waitingfor its result. In this case it is recommended to use [`IOLoop.spawn_callback`](# "tornado.ioloop.IOLoop.spawn_callback"),which makes the [`IOLoop`](# "tornado.ioloop.IOLoop") responsible for the call. If it fails,the [`IOLoop`](# "tornado.ioloop.IOLoop") will log a stack trace: ~~~ # The IOLoop will catch the exception and print a stack trace in # the logs. Note that this doesn't look like a normal call, since # we pass the function object to be called by the IOLoop. IOLoop.current().spawn_callback(divide, 1, 0) ~~~ Finally, at the top level of a program, *if the `.IOLoop` is not yetrunning,* you can start the [`IOLoop`](# "tornado.ioloop.IOLoop"), run the coroutine, and thenstop the [`IOLoop`](# "tornado.ioloop.IOLoop") with the [`IOLoop.run_sync`](# "tornado.ioloop.IOLoop.run_sync") method. This is oftenused to start the `main` function of a batch-oriented program: ~~~ # run_sync() doesn't take arguments, so we must wrap the # call in a lambda. IOLoop.current().run_sync(lambda: divide(1, 0)) ~~~ ### Coroutine patterns ### Interaction with callbacks To interact with asynchronous code that uses callbacks instead of[`Future`](# "tornado.concurrent.Future"), wrap the call in a [`Task`](# "tornado.gen.Task"). This will add the callbackargument for you and return a [`Future`](# "tornado.concurrent.Future") which you can yield: ~~~ @gen.coroutine def call_task(): # Note that there are no parens on some_function. # This will be translated by Task into # some_function(other_args, callback=callback) yield gen.Task(some_function, other_args) ~~~ ### Calling blocking functions The simplest way to call a blocking function from a coroutine is touse a [`ThreadPoolExecutor`](https://docs.python.org/3.4/library/concurrent.futures.html#concurrent.futures.ThreadPoolExecutor "(in Python v3.4)") [https://docs.python.org/3.4/library/concurrent.futures.html#concurrent.futures.ThreadPoolExecutor], which returns`Futures` that are compatible with coroutines: ~~~ thread_pool = ThreadPoolExecutor(4) @gen.coroutine def call_blocking(): yield thread_pool.submit(blocking_func, args) ~~~ ### Parallelism The coroutine decorator recognizes lists and dicts whose values are`Futures`, and waits for all of those `Futures` in parallel: ~~~ @gen.coroutine def parallel_fetch(url1, url2): resp1, resp2 = yield [http_client.fetch(url1), http_client.fetch(url2)] @gen.coroutine def parallel_fetch_many(urls): responses = yield [http_client.fetch(url) for url in urls] # responses is a list of HTTPResponses in the same order @gen.coroutine def parallel_fetch_dict(urls): responses = yield {url: http_client.fetch(url) for url in urls} # responses is a dict {url: HTTPResponse} ~~~ ### Interleaving Sometimes it is useful to save a [`Future`](# "tornado.concurrent.Future") instead of yielding itimmediately, so you can start another operation before waiting: ~~~ @gen.coroutine def get(self): fetch_future = self.fetch_next_chunk() while True: chunk = yield fetch_future if chunk is None: break self.write(chunk) fetch_future = self.fetch_next_chunk() yield self.flush() ~~~ ### Looping Looping is tricky with coroutines since there is no way in Pythonto `yield` on every iteration of a `for` or `while` loop andcapture the result of the yield. Instead, you'll need to separatethe loop condition from accessing the results, as in this examplefrom [Motor](http://motor.readthedocs.org/en/stable/) [http://motor.readthedocs.org/en/stable/]: ~~~ import motor db = motor.MotorClient().test @gen.coroutine def loop_example(collection): cursor = db.collection.find() while (yield cursor.fetch_next): doc = cursor.next_object() ~~~ ### Running in the background [`PeriodicCallback`](# "tornado.ioloop.PeriodicCallback") is not normally used with coroutines. Instead, acoroutine can contain a `while True:` loop and use[`tornado.gen.sleep`](# "tornado.gen.sleep"): ~~~ @gen.coroutine def minute_loop(): while True: yield do_something() yield gen.sleep(60) # Coroutines that loop forever are generally started with # spawn_callback(). IOLoop.current().spawn_callback(minute_loop) ~~~ Sometimes a more complicated loop may be desirable. For example, theprevious loop runs every `60+N` seconds, where `N` is the runningtime of `do_something()`. To run exactly every 60 seconds, use theinterleaving pattern from above: ~~~ @gen.coroutine def minute_loop2(): while True: nxt = gen.sleep(60) # Start the clock. yield do_something() # Run while the clock is ticking. yield nxt # Wait for the timer to run out. ~~~ © Copyright 2009-2016, The Tornado Authors. Created using [Sphinx](http://sphinx-doc.org/) 1.3.5.