]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
Add an FAQ page. branch4.1
authorBen Darnell <ben@bendarnell.com>
Wed, 4 Mar 2015 05:01:05 +0000 (00:01 -0500)
committerBen Darnell <ben@bendarnell.com>
Wed, 4 Mar 2015 05:01:05 +0000 (00:01 -0500)
docs/faq.rst [new file with mode: 0644]
docs/index.rst

diff --git a/docs/faq.rst b/docs/faq.rst
new file mode 100644 (file)
index 0000000..d45173c
--- /dev/null
@@ -0,0 +1,96 @@
+Frequently Asked Questions
+==========================
+
+.. contents::
+   :local:
+
+Why isn't this example with ``time.sleep()`` running in parallel?
+-----------------------------------------------------------------
+
+Many people's first foray into Tornado's concurrency looks something like
+this::
+
+   class BadExampleHandler(RequestHandler):
+       def get(self):
+           for i in range(5):
+               print(i)
+               time.sleep(1)
+
+Fetch this handler twice at the same time and you'll see that the second
+five-second countdown doesn't start until the first one has completely
+finished. The reason for this is that `time.sleep` is a **blocking**
+function: it doesn't allow control to return to the `.IOLoop` so that other
+handlers can be run.
+
+Of course, `time.sleep` is really just a placeholder in these examples,
+the point is to show what happens when something in a handler gets slow.
+No matter what the real code is doing, to achieve concurrency blocking
+code must be replaced with non-blocking equivalents. This means one of three things:
+
+1. *Find a coroutine-friendly equivalent.* For `time.sleep`, use
+   `tornado.gen.sleep` instead::
+
+    class CoroutineSleepHandler(RequestHandler):
+        @gen.coroutine
+        def get(self):
+            for i in range(5):
+                print(i)
+                yield gen.sleep(1)
+
+   When this option is available, it is usually the best approach.
+   See the `Tornado wiki <https://github.com/tornadoweb/tornado/wiki/Links>`_
+   for links to asynchronous libraries that may be useful.
+
+2. *Find a callback-based equivalent.* Similar to the first option,
+   callback-based libraries are available for many tasks, although they
+   are slightly more complicated to use than a library designed for
+   coroutines. These are typically used with `tornado.gen.Task` as an
+   adapter::
+
+    class CoroutineTimeoutHandler(RequestHandler):
+        @gen.coroutine
+        def get(self):
+            io_loop = IOLoop.current()
+            for i in range(5):
+                print(i)
+                yield gen.Task(io_loop.add_timeout, io_loop.time() + 1)
+
+   Again, the
+   `Tornado wiki <https://github.com/tornadoweb/tornado/wiki/Links>`_
+   can be useful to find suitable libraries.
+
+3. *Run the blocking code on another thread.* When asynchronous libraries
+   are not available, `concurrent.futures.ThreadPoolExecutor` can be used
+   to run any blocking code on another thread. This is a universal solution
+   that can be used for any blocking function whether an asynchronous
+   counterpart exists or not::
+
+    executor = concurrent.futures.ThreadPoolExecutor(8)
+
+    class ThreadPoolHandler(RequestHandler):
+        @gen.coroutine
+        def get(self):
+            for i in range(5):
+                print(i)
+                yield executor.submit(time.sleep, 1)
+
+See the :doc:`Asynchronous I/O <guide/async>` chapter of the Tornado
+user's guide for more on blocking and asynchronous functions.
+
+
+My code is asynchronous, but it's not running in parallel in two browser tabs.
+------------------------------------------------------------------------------
+
+Even when a handler is asynchronous and non-blocking, it can be surprisingly
+tricky to verify this. Browsers will recognize that you are trying to
+load the same page in two different tabs and delay the second request
+until the first has finished. To work around this and see that the server
+is in fact working in parallel, do one of two things:
+
+* Add something to your urls to make them unique. Instead of
+  ``http://localhost:8888`` in both tabs, load
+  ``http://localhost:8888/?x=1`` in one and
+  ``http://localhost:8888/?x=2`` in the other.
+
+* Use two different browsers. For example, Firefox will be able to load
+  a url even while that same url is being loaded in a Chrome tab.
index 609f3671cc9f0a088aa46074837439e74d4a3031..92dc18230369b0a7cdf09a64c495d196b8a146f2 100644 (file)
@@ -129,6 +129,7 @@ This documentation is also available in `PDF and Epub formats
    coroutine
    integration
    utilities
+   faq
    releases
 
 * :ref:`genindex`