正在加载...

tornado-12 实现自己的gen.engine和gen.Task

在之前,我们有说过tornado的web异步调用。今天我们来分析一下。

先看下面的代码。

# -*- coding:utf-8 -*-
from tornado import ioloop, httpclient

def deal(response):
    print 'response.length =', len(response.body)
    ioloop.IOLoop.instance().stop()


def download(url):
    http_client = httpclient.AsyncHTTPClient()
    http_client.fetch(url, deal)


download("http://www.baidu.com/")    
ioloop.IOLoop.instance().start()

结果如下:

response.length = 10233

很明显的可以看出。http_client的fetch只是一个异步调用。需要传递一个deal方法,作为callback来去处理fetch后的response。

如果我们用tornado的gen.engine方式可以这样写:

from tornado import ioloop, httpclient, gen
from tornado.gen import Task

@gen.engine
def download(url):
    http_client = httpclient.AsyncHTTPClient()
    response = yield Task(http_client.fetch, url)
    print 'response.length =', len(response.body) 
    ioloop.IOLoop.instance().stop()

download("http://www.baidu.com/")

ioloop.IOLoop.instance().start()

这样就好像你在写同步代码一样。而不需要想之前那样处理回调了。

下面我们考虑,如果要你实现一个gen.engine 和 Task 你会怎么实现呢?

根据我们之前谈的yield 和 generator,我们可以这样写代码:

# -*- coding:utf-8 -*-
from tornado import ioloop, httpclient
import functools


class MyTask(object):

    def __init__(self, func, *args, **kwargs):
        self.func = func
        self.args = args
        self.kwargs = kwargs

    def callback(self, response):
        try:
            self.gen.send(response)
        except StopIteration:
            pass

    def run(self, gen):
        self.gen = gen
        partail_func = functools.partial(self.func, *self.args, **self.kwargs)
        partail_func(callback = self.callback)


def myengine(func):
    def _(*args, **kwargs):
        task_generator = func(*args, **kwargs)
        task = task_generator.next()
        task.run(task_generator)
    return _


@myengine
def download(url):
    http_client = httpclient.AsyncHTTPClient()
    response = yield MyTask(http_client.fetch, url)
    print 'response.length =', len(response.body)
    ioloop.IOLoop.instance().stop()


download("http://www.baidu.com/")
ioloop.IOLoop.instance().start()

这个时候,结果是:

response.length = 10233

这样,属于我们自己的engine和task就出来了。