Future的主要用途就是,存儲結果。當結果返回時,就會對調註冊的函數。用於支持異步,是很是方便的。
python
def async_task(): future = Future() ioloop.IOLoop.current().call_later(lambda f: f.set_result('hello')) return future
定義對調函數,回調函數必須接受future參數。app
def callback(future): try: result = future.result() except: print "exception occured" print "future return result: %s" % result
註冊回調函數:異步
future = async_task() future.set_done_callback(callback)
首先來看註冊回調函數的源碼:async
def add_done_callback(self, fn): """Attaches the given callback to the `Future`. It will be invoked with the `Future` as its argument when the Future has finished running and its result is available. In Tornado consider using `.IOLoop.add_future` instead of calling `add_done_callback` directly. """ if self._done: fn(self) else: self._callbacks.append(fn)
self就是指向Future自身,由fn(self),能夠看出回調函數的格式。ide
若是已經返回結果或者異常,就直接調用這個回調函數fn。函數
若是沒有,就把fn添加到self._callbacks屬性裏。等到結果或者異常返回時,再調用。tornado
而後接着看,怎麼返回結果:
oop
def set_result(self, result): """Sets the result of a ``Future``. It is undefined to call any of the ``set`` methods more than once on the same object. """ self._result = result self._set_done()
這裏將結果保存在self._result屬性裏,接着調用_set_done方法。spa
def _set_done(self): self._done = True for cb in self._callbacks: try: cb(self) except Exception: app_log.exception('exception calling callback %r for %r', cb, self) self._callbacks = None
更新_done屬性,而後依次調用self._callbacks的回調函數。code
最後跟新self._callbacks爲None。
若是是返回異常:
def set_exception(self, exception): """Sets the exception of a ``Future.``""" self.set_exc_info( (exception.__class__, exception, getattr(exception, '__traceback__', None))) def set_exc_info(self, exc_info): """Sets the exception information of a ``Future.`` Preserves tracebacks on Python 2. .. versionadded:: 4.0 """ self._exc_info = exc_info self._log_traceback = True if not _GC_CYCLE_FINALIZERS: self._tb_logger = _TracebackLogger(exc_info) try: self._set_done() finally: # Activate the logger after all callbacks have had a # chance to call result() or exception(). if self._log_traceback and self._tb_logger is not None: self._tb_logger.activate() self._exc_info = exc_info
set_exception就是調用了set_exc_info。
一樣也能夠看出,set_exc_info函數的參數exc_info,是一個長度爲3的元祖,(class, instance, information)。
這裏的_log_traceback和,_tb_logger是用來防止有異常發生,若是沒有人嘗試獲取,異常不會被正常拋出。
後面有詳細的解釋。
能夠看到self.set_exc_info和self.set_result方法,都是更新相應的結果屬性,而後調用回調函數。
接着看看如何從future中獲取結果
def result(self, timeout=None): """If the operation succeeded, return its result. If it failed, re-raise its exception. """ self._clear_tb_log() if self._result is not None: return self._result if self._exc_info is not None: raise_exc_info(self._exc_info) self._check_done() return self._result
self._clear_tb_log方法是從新初始化self._log_traceback和self._tb_logger。
若是有結果,就返回_result。若是有異常,就拋出異常。
若是都沒有,說明有兩種狀況。
異步的程序尚未完成,沒有結果或者異常返回。
異步的程序已經完成,只不過返回的結果就是None
因此纔會有self.check_done方法,檢查。
def _check_done(self): if not self._done: raise Exception("DummyFuture does not support blocking for results")
若是是第一種,就會拋出異常。說明self.result方法不支持阻塞等待結果返回的。一樣也說明,直接獲取result,必須在保證future已經返回結果或異常了。否則,得須要調用done接口判斷
def done(self): """Returns True if the future has finished running.""" return self._done
若是是第二種,直接返回self._result
獲取異常:
#獲取異常 def exception(self, timeout=None): """If the operation raised an exception, return the `Exception` object. Otherwise returns None. """ self._clear_tb_log() if self._exc_info is not None: return self._exc_info[1] else: self._check_done() return None #獲取異常信息 def exc_info(self): """Returns a tuple in the same format as `sys.exc_info` or None. .. versionadded:: 4.0 """ self._clear_tb_log() return self._exc_info
exception一樣不支持阻塞等待。不過exception的值是不可能爲空的。因此,就存在兩種狀況。
self._exc_info不爲空,返回異常實例
self._exc_info爲空, 說明要麼異步沒有完成,要麼異步完成,成功返回結果。
def exc_info(self): """Returns a tuple in the same format as `sys.exc_info` or None. .. versionadded:: 4.0 """ self._clear_tb_log() return self._exc_info
exc_info方法直接返回self._exc_info。
最後再來看看_TracebackLogger的原理:
爲首先看下這個狀況,當future返回異常,但由於程序沒有嘗試獲取異常,那麼這個異常就會被默默的遺棄,那麼怎麼才能讓這個異常讓用戶知道。
有人會說在__del__方法裏,調用app_log.error記錄錯誤信息。由於__del__方法,在對象被回收時,會自動調用。
可是在python3.4之前,若是對象與對象有着相互引用,那麼它們的__del__方法,是不會被調用的。
也叫是說這個__del__方法不必定會被調用。那麼,tornado又是怎樣實現的呢?
每一個Future對象會只對應一個_TracebackLogger對象,它們之間是一對一的關係。當Future被回收時,——_TracebackLogger也會被回收,由於_TracebackLogger只會對應一個Future對象,因此_TracebackLogger的__del__方法,確定會被調用。
class _TracebackLogger(object): __slots__ = ('exc_info', 'formatted_tb') def __init__(self, exc_info): self.exc_info = exc_info self.formatted_tb = None def activate(self): exc_info = self.exc_info if exc_info is not None: self.exc_info = None self.formatted_tb = traceback.format_exception(*exc_info) def clear(self): self.exc_info = None self.formatted_tb = None def __del__(self): if self.formatted_tb: app_log.error('Future exception was never retrieved: %s', ''.join(self.formatted_tb).rstrip())
很簡單的幾個方法,調用activate會更新self.formatted_tb屬性。而後__del__方法會根據這個屬性,判斷是否記錄log。