源代码在:http://www.tornadoweb.org/en/stable/_modules/tornado/concurrent.html#Future。
Future用来囊括异步操作的结果。在tornado中,Future通常和IOLoop.add_future()
一起使用,或者由gen.coroutine
修饰的生成器函数yield它们。
当调用一个异步操作的时候,它会立即返回一个Future对象。然后,调用方在返回的Future对象上,设置回调函数。异步操作会将自己注册到IOLoop事件循环上,每次事件循环,都会“向前推进”异步操作,当异步操作完成时,它会将结果或异常信息,填充到Future对象中,并且会使用该Future对象作为参数 调用 该Future对象上设置的回调函数。所以,本质上,Future就是一个“占位符”。
需要注意的是:Future是非线程安全的。
class Future(object): def __init__(self): self._done = False # 异步操作是否执行完成 self._result = None # 如果异步操作成功执行完成,则将结果保存到self._result self._exc_info = None # 如果异步操作执行过程中出现异常,则将异常信息保存到self._exc_info ... self._callbacks = [] # 回调函数列表。在异步操作执行完成之后,会挨个调用这些回调函数 def running(self): """Returns True if this operation is currently running.""" return not self._done def done(self): """Returns True if the future has finished running.""" return self._done def result(self, timeout=None): """If the operation succeeded, return its result. If it failed, re-raise its exception. This method takes a ``timeout`` argument for compatibility with `concurrent.futures.Future` but it is an error to call it before the `Future` is done, so the ``timeout`` is never used. """ # 如果异步操作成功执行,则返回它的结果 if self._result is not None: return self._result # 如果异步操作执行失败,则重新抛出异常信息 if self._exc_info is not None: try: raise_exc_info(self._exc_info) finally: self = None # 如果异步操作尚未完成,则等待它执行结束,然后返回它的结果 # + !!!但是在tornado中没有实现阻塞的等待结果, # + + 如果异步操作没有完成,调用该方法会引发异常!!! # + 因此,最好是:# if future.done(): # result = future.result() # ...self._check_done() return self._result def exception(self, timeout=None): """If the operation raised an exception, return the `Exception` object. Otherwise returns None. This method takes a ``timeout`` argument for compatibility with `concurrent.futures.Future` but it is an error to call it before the `Future` is done, so the ``timeout`` is never used. """ # 如果异步操作抛出了异常,那么就返回异常对象 if self._exc_info is not None: return self._exc_info[1] else: # 如果异步操作尚未完成,则抛出异常。 # 如果异步操作成功执行,则返回None。 self._check_done() return None 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) 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. """ # 如果该Future对象所绑定的异步操作,成功执行了,在异步操作中 # 应该调用Future对象的set_result()方法设置执行结果。 # 该方法会将结果保存起来,并且将Future对象设置为完成, # + 最后,还会挨个调用回调函数 self._result = result self._set_done() 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属性 self._exc_info = exc_info try: # 调用_set_done()将该Future对象标记位完成;并调用所有的回调函数 self._set_done() finally: ... self._exc_info = exc_info def _check_done(self): # 因为在tornado的这个实现中,不支持阻塞等待异步操作的结果。 # + 所以,当异步操作尚未完成时,会直接抛出异常。 if not self._done: raise Exception("DummyFuture does not support blocking for results") def _set_done(self): self._done = True for cb in self._callbacks: try: cb(self) except Exception: app_log.exception('Exception in callback %r for %r', cb, self) self._callbacks = None