Future的主要用途就是,存储结果。当结果返回时,就会对调注册的函数。用于支持异步,是非常方便的。
1
2
3
4
|
def
async_task():
future
=
Future()
ioloop.IOLoop.current().call_later(
lambda
f: f.set_result(
'hello'
))
return
future
|
定义对调函数,回调函数必须接受future参数。
1
2
3
4
5
6
|
def
callback(future):
try
:
result
=
future.result()
except
:
print
"exception occured"
print
"future return result: %s"
%
result
|
注册回调函数:
1
2
|
future
=
async_task()
future.set_done_callback(callback)
|
首先来看注册回调函数的源码:
1
2
3
4
5
6
7
8
9
10
11
12
|
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),可以看出回调函数的格式。
如果已经返回结果或者异常,就直接调用这个回调函数fn。
如果没有,就把fn添加到self._callbacks属性里。等到结果或者异常返回时,再调用。
然后接着看,怎么返回结果:
1
2
3
4
5
6
7
8
|
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方法。
1
2
3
4
5
6
7
8
9
|
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的回调函数。
最后跟新self._callbacks为None。
如果是返回异常:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
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中获取结果
1
2
3
4
5
6
7
8
9
10
11
|
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方法,检查。
1
2
3
|
def
_check_done(
self
):
if
not
self
._done:
raise
Exception(
"DummyFuture does not support blocking for results"
)
|
如果是第一种,就会抛出异常。说明self.result方法不支持阻塞等待结果返回的。同样也说明,直接获取result,必须在保证future已经返回结果或异常了。不然,得需要调用done接口判断
1
2
3
|
def
done(
self
):
"""Returns True if the future has finished running."""
return
self
._done
|
如果是第二种,直接返回self._result
获取异常:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#获取异常
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为空, 说明要么异步没有完成,要么异步完成,成功返回结果。
1
2
3
4
5
6
7
|
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__方法,肯定会被调用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
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。