I am trying to understand the basic example provided on the introduction page of tornado documentation. It has 2 blocks of code. The Synchronous one is fine for me, and I do
The idea of asynchronous calls is something that works pretty much the same in many web related programming... "stuff" (frameworks, servers, libraries...) Is not only a concept from the Tornado web server.
The basic idea is:
On a̲s̲y̲n̲c̲h̲r̲o̲n̲o̲u̲s̲ requests, you "launch" the request, and you kind of "forget about it", meaning: The interpreter continues executing the code after the request is made without waiting for the request to be completed.
This seems... rather pointless, right? You send the request "to the void of space", and continue executing as usual? What happens when the server sends you its response? I made a request, and I wanna know what happened to it! Otherwise, I wouldn't have typed that in my code to begin with!!
Well, here's where the callback
comes in. You launch the request "to the void of space" BUT you provide a callback function so when the HTTP server on the other end sends you its response, that function is run with said response
as the first argument.
Let's see it with a en example.
I've created a very simple Tornado server (using Python 2.7
and Tornado 4.2
) with only one handler. On a GET
, it takes 5 seconds to return. I've done that with a time.sleep, but in real life, it could be a very time consuming process (access a database, perform some calculations... who knows?...)
Here's the server file (based on the example provided in the Tornado documentation):
SampleServer.py
:#!/usr/bin/env python2.7
import time
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self):
print "Someone is GET'ing me"
time.sleep(5)
self.write("Hello, world")
application = tornado.web.Application([
(r"/", MainHandler),
])
if __name__ == "__main__":
application.listen(8888)
print "Starting sample server."
tornado.ioloop.IOLoop.current().start()
Open a terminal and run that code to have a server. You'll get a Tornado listening on port 8888
of your local machine.
Now, let's create another script (which you'll have to run in another terminal) that GET
s http://localhost:8888
in two ways: First synchronously and then asynchronously.
SampleFetcher.py
:#!/usr/bin/env python2.7
import datetime
import tornado.ioloop
from tornado.httpclient import HTTPClient, AsyncHTTPClient
def HUMAN_DT_NOW():
return datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S")
def synchronous_fetch(url):
http_client = HTTPClient()
start_dt = datetime.datetime.now()
response = http_client.fetch(url)
end_dt = datetime.datetime.now()
print ("The synchronous fetch took %s seconds."
% (end_dt - start_dt).total_seconds())
print "(Sync) Server said: \"%s\"" % response.body
def asynchronous_fetch(url):
http_client = AsyncHTTPClient()
def handle_response(response):
print ""
print "Yawwza... Finally!!!."
print "The time now is %s" % HUMAN_DT_NOW()
print "(Async) Server said: \"%s\"" % response.body
print "Gonna launch a 'fetch' to the universe at %s..." % HUMAN_DT_NOW()
http_client.fetch(url, callback=handle_response)
if __name__ == "__main__":
print " ------ Synchronous ------ "
print ("Starting synchronous fetch at %s."
" The program will block for about 5 secs." % HUMAN_DT_NOW())
synchronous_fetch('http://localhost:8888')
print "Pfew! That was a lot of wait time!!. I got bored watching my terminal"
print ""
print "Aight, let's see what Asynchronous can do"
print " ------ Asynchronous ------ "
asynchronous_fetch('http://localhost:8888')
print "You're gonna see this line before the \"Yawwza...\" one"
print "This one too. Now is %s" % HUMAN_DT_NOW()
# The IOLoop below is required to prevent the script from closing ahead
# of time, but allowing Asynchronous interactions
tornado.ioloop.IOLoop.current().start()
This will output:
Starting synchronous fetch at 2015/07/04 13:25:47. The program will block for about 5 secs.
The synchronous fetch took 5.009597 seconds.
(Sync) Server said: "Hello, world"
Pfew! That was a lot of wait time!!. I got bored watching my terminal
Aight, let's see what Asynchronous can do
------ Asynchronous ------
Gonna launch a 'fetch' to the universe at 2015/07/04 13:25:52...
You're gonna see this line before the "Yawwza..." one
This one too. Now is 2015/07/04 13:25:52
Yawwza... Finally!!!.
The time now is 2015/07/04 13:25:57
(Async) Server said: "Hello, world"
Let's focus on the asynchronous part, here:
------ Asynchronous ------
Gonna launch a 'fetch' to the universe at 2015/07/04 13:25:52...
You're gonna see this line before the "Yawwza..." one
This one too. Now is 2015/07/04 13:25:52
Yawwza... Finally!!!.
The time now is 2015/07/04 13:25:57
(Async) Server said: "Hello, world"
If you see, the script made the asynchronous_fetch
at 13:25:52
, but immediately (in the same second), the interpreter continued executing, and run the next statements after the request was made (the lines that print You're gonna see this line before the "Yawwza..." one
and This one too. Now is 2015/07/04 13:25:52
).
Then, around 5 seconds later, the server responded, and the callback
function (which was handle_response
) was executed, and that's when you see
Yawwza... Finally!!!.
The time now is 2015/07/04 13:25:57
I hope this helped understanding the idea a bit. It's a very useful concept, and it doesn't only apply to Tornado.
Feel free to play with the two sample scripts provided, change stuff, increase the times it gets for the server to reply...
Further recommended reading: