1. yield的基本概念html
2. Future的用法python
3. ioloop的經常使用接口app
4. gen.coroutine的應用異步
5. gen.coroutine的源碼async
6. Runner類的實現函數
1. yield的基本概念oop
python中使用yield實現了生成器函數,一樣yield.send( )方法也實現了控制權在函數間的跳轉。fetch
關於yield比較詳細的介紹,能夠參考這篇博文http://www.cnblogs.com/coderzh/articles/1202040.html spa
2.Future的用法code
Future是用來在異步過程當中,存放結果的容器。它能夠在結果出來時,進行回調。
add_done_callback(self, fn): 添加回調函數fn,函數fn必須以future做爲參數。
set_result(self, result): 存放結果,此函數會執行回調函數。
set_exception(self, exception):存放異常,此函數會執行回調函數。
3. ioloop的經常使用接口
add_future(self, future, callback): 當future返回結果時,會將callback登記到ioloop中,由ioloop在下次輪詢時調用。
add_callback(self, callback, *args, **kwargs): 將callback登記到ioloop中,由ioloop在下次輪詢時調用。
add_timeout(self, deadline, callback, *args, **kwargs): 將callback登記到ioloop中,由ioloop在指定的deadline時間調用。
def call_at(self, when, callback, *args, **kwargs): 將callback登記到ioloop中,由ioloop在指定的when時間調用。
def call_later(self, delay, callback, *args, **kwargs): 將callback登記到ioloop中, 由ioloop在指定的延遲delay秒後調用。
add_handler(self, fd, handler, events): 將文件符fd的event事件和回調函數handler登記到ioloop中,當事件發生時,觸發。
4. gen.coroutine的應用
直接上官網的例子
普通的回調函數的方式:
class AsyncHandler(RequestHandler): @asynchronous def get(self): http_client = AsyncHTTPClient() http_client.fetch("http://example.com", callback=self.on_fetch) def on_fetch(self, response): do_something_with_response(response) self.render("template.html")
同步的方式:
class GenAsyncHandler(RequestHandler): @gen.coroutine def get(self): http_client = AsyncHTTPClient() response = yield http_client.fetch("http://example.com") do_something_with_response(response) self.render("template.html")
能夠看出代碼明顯清晰,簡單多了。若是有深層次的回調,效果會更明顯。
5. gen.coroutine的源碼
def coroutine(func, replace_callback=True): return _make_coroutine_wrapper(func, replace_callback=True)
接着看_make_coroutine_wrapper的源碼:
def _make_coroutine_wrapper(func, replace_callback): @functools.wraps(func) def wrapper(*args, **kwargs): future = TracebackFuture() if replace_callback and 'callback' in kwargs: callback = kwargs.pop('callback') IOLoop.current().add_future( future, lambda future: callback(future.result())) try: result = func(*args, **kwargs) except (Return, StopIteration) as e: result = getattr(e, 'value', None) except Exception: future.set_exc_info(sys.exc_info()) return future else: if isinstance(result, types.GeneratorType): # Inline the first iteration of Runner.run. This lets us # avoid the cost of creating a Runner when the coroutine # never actually yields, which in turn allows us to # use "optional" coroutines in critical path code without # performance penalty for the synchronous case. try: orig_stack_contexts = stack_context._state.contexts yielded = next(result) if stack_context._state.contexts is not orig_stack_contexts: yielded = TracebackFuture() yielded.set_exception( stack_context.StackContextInconsistentError( 'stack_context inconsistency (probably caused ' 'by yield within a "with StackContext" block)')) 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) try: return future finally: future = None future.set_result(result) return future return wrapper
這段代碼注意到,有幾個關鍵地方。
future = TracebackFuture()
TracebackFuture就是Future,二者是同一個類。這裏定義了future變量, 是用來後面增長callback用的。當函數執行完時,會將這個future返回。
result = func(*args, **kwargs)
這裏執行被裝飾的函數,返回result。咱們知道有yield語句的函數,返回結果是一個生成器。
yielded = next(result)
返回yield結果, next(result)被要求返回Future,list, dict,或者YieldPoint類型,通常返回Future。
Runner(result, future, yielded)
實例化Runner,這步很重要。
return future
返回future。
6. Runner類的實現
class Runner(object): def __init__(self, gen, result_future, first_yielded): self.gen = gen self.result_future = result_future self.future = _null_future self.yield_point = None self.pending_callbacks = None self.results = None self.running = False self.finished = False self.had_exception = False self.io_loop = IOLoop.current() self.stack_context_deactivate = None if self.handle_yield(first_yielded): self.run()
__init__構造函數首先初始化參數,而後調用handle_yield方法。
由於first_yielded爲Future類型, 因此簡化下handle_yield的代碼。
def handle_yield(self, yielded): self.future = yielded if not self.future.done(): self.io_loop.add_future( self.future, lambda f: self.run()) return False return True
handle_yield方法,首先判斷yielded是否已經返回結果,若是沒有就調用io_loop.add_future方法。這樣當yielded返回結果時,就會回調self.run方法。若是已經返回,就直接執行self.run方法。
self.run方法會將控制權返回給被gen.coroutine的函數。
def run(self): """Starts or resumes the generator, running until it reaches a yield point that is not ready. """ if self.running or self.finished: return try: self.running = True while True: future = self.future if not future.done(): return self.future = None try: orig_stack_contexts = stack_context._state.contexts try: value = future.result() except Exception: self.had_exception = True yielded = self.gen.throw(*sys.exc_info()) else: yielded = self.gen.send(value) if stack_context._state.contexts is not orig_stack_contexts: self.gen.throw( stack_context.StackContextInconsistentError( 'stack_context inconsistency (probably caused ' 'by yield within a "with StackContext" block)')) except (StopIteration, Return) as e: self.finished = True self.future = _null_future if self.pending_callbacks and not self.had_exception: # If we ran cleanly without waiting on all callbacks # raise an error (really more of a warning). If we # had an exception then some callbacks may have been # orphaned, so skip the check in that case. raise LeakedCallbackError( "finished without waiting for callbacks %r" % self.pending_callbacks) self.result_future.set_result(getattr(e, 'value', None)) self.result_future = None self._deactivate_stack_context() return except Exception: self.finished = True self.future = _null_future self.result_future.set_exc_info(sys.exc_info()) self.result_future = None self._deactivate_stack_context() return if not self.handle_yield(yielded): return finally: self.running = False
這裏注意到幾個關鍵地方。
value = future.result()
獲取yielded的結果值。
yielded = self.gen.send(value)
使用send方法將結果返回,而且將執行權交給被裝飾的函數。而且返回下一個yield的值。
若是函數生成器後面沒有yield語句了,就會拋出StopIteration異常。
調用set_result存儲結果,返回。
self.result_future.set_result(getattr(e, 'value', None))
若是函數生成器後面還有yield語句,就會調用handle_yield,仍然登記self.run方法。
if not self.handle_yield(yielded): return
之因此要有個判斷,是須要在while循環裏,若是future已經有返回結果了,就繼續取結果,send結果,
一直到future的結果沒有返回或者沒有yield語句了。