Tornado中gen.coroutine詳解

1.gen.coroutine的做用
自動執行生成器javascript

2.Future對象
在介紹異步使用以前,先了解一下Future對象的做用。
Future簡單能夠理解爲一個佔位符,未來會執行的對象,相似javascript中的promise對象,是實現異步的關鍵。java

class Future(object):
    def __init__(self):
        self._callback = []
        self._result = None
        self._done = False

    def set_callback(self, cb):
        self._callback.append(cb)

    def _run_callback(self):
        for cb in self._callback:
            cb()

    def set_result(self, result)
        self._done = True
        self._result = result
        self._run_callback()

    def is_ready(self):
        return self._done is True

_result:返回結果值
_done:是否完成
_callback:完成後,執行的回調列表
set_result():賦值result,Future對象完成,執行回調。python

3.callback實現異步web

from tornado.httpclient import AsyncHTTPClient
from tornado.web import RequestHandler


class Test1Handler(RequestHandler):
    def get(self, *args, **kwargs):
        http_client = AsyncHTTPClient()
        http_client.fetch('www.baidu.com', callback = self.on_fetched)
        print('done')

    def on_fetched(self, response):
        print('response')

運行結果:
promise

源碼分析:app

def fetch(self, request, callback=None, raise_error=True, **kwargs):
    if callback is not None:
        def handle_future(future):
            response = future.result()
            self.io_loop.add_callback(callback, response)
        future.add_done_callback(handle_future)
    def handle_response(response):
        if raise_error and response.error:
            future.set_exception(response.error)
        else:
            future.set_result(response)
    self.fetch_impl(request, handle_response)
    return future   

def fetch_impl(self, request, callback):
    raise NotImplementedError()

fetch函數返回一個Future類型對象,fetch_impl()執行完畢,返回結果response做爲參數,執行回調handle_response
handle_response將response賦值給future。future狀態變爲已完成,執行future的callback函數handle_future,handle_future將callback加入ioloop執行隊列,response做爲參數。
由ioloop調度完成callback。
關鍵點在於,Future佔位符控制了何時執行回調。異步

3.gen.coroutine實現異步函數

from tornado.httpclient import AsyncHTTPClient
from tornado.web import RequestHandler
from tornado import gen


class Test1Handler(RequestHandler):
    @gen.coroutine
    def get(self, *args, **kwargs):
        http_client = AsyncHTTPClient()
        response = yield http_client.fetch('http://www.baidu.com')
        print('response')

運行結果:
tornado

當執行到yield 表達式時,表達式會返回一個Future佔位符,而後返回,當表達式執行完畢後,自動繼續執行生成器。
關鍵點在於,gen.coroutine使生成器能夠自動執行。
源碼分析:oop

def coroutine(func, replace_callback=True):
    return _make_coroutine_wrapper(func, replace_callback=True)
def _make_coroutine_wrapper(func, replace_callback):
    try:
        yielded = next(result)
    except (StopIteration, Return) as e:
        future.set_result(getattr(e, 'value', None))
    except Exception:
        future.set_exc_info(sys.exc_info())
    else:
        Runner(result, future, yielded)

result:生成器對象
yielded:Future對象,生成器首次執行結果,若是異常StopIteration,表示生成器執行完畢,將結果設置成future的值,返回,裝飾器gen.coroutine返回的爲Future對象。
Runner:判斷yielded是否完成,完成則執行run函數,繼續執行生成器;不然,添加run函數到這個Future對象yielded,執行完畢以後,才調用run函數。

class Runner(object):
    def __init__(self, gen, result_future, first_yielded):
        if self.handle_yield(first_yielded):
            gen = result_future = first_yielded = None
            self.run()

    def handle_yield(self, yielded):
        self.future = convert_yielded(yielded)

        if not self.future.done() or self.future is moment:
            self.io_loop.add_future(
                self.future, lambda f: self.run())
            return False
        return True

    def run(self):
        if self.running or self.finished:
            return
        try:
            self.running = True
            while True:
                future = self.future
                if not future.done(): #執行run時generator返回的那個future必須已經有結果,不然就不必傳回到generator中了
                    return
                self.future = None
                try:
                    value = future.result()
                    yielded = self.gen.send(value)
                except (StopIteration, Return) as e:
                    #generator執行完畢併成功的處理
                except Exception:
                    #generator執行過程當中異常的處理
                if not self.handle_yield(yielded):
                    return
        finally:
            self.running = False

handle_yield:判斷Future對象yielded是否完成,未完成,註冊run()函數回調到這個Future對象,完成,才調用。 run:將yielded這個Future對象的result,做爲參數傳遞給生成器,繼續執行生成器。

相關文章
相關標籤/搜索