源代码在: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