跟踪 twisted 里deferred 的Callback
twisted 提供了 deferred 机制,而关键点就是回调。通过查看deferred 源码 (version 8.2.0)我们可以 看到
deferred的addCallback是怎么工作的,以及相关的操作流程。 一个deferred 有一对回调链,一条处理正确的结果,一条处理错误的结果,addCallbacks 与 addCallback ,addBoth 函数则是向这对回调链里添加函数,addCallback 后,deferred 是怎么做的呢,我们看源码:
def addCallbacks(self, callback, errback=None,
callbackArgs=None, callbackKeywords=None,
errbackArgs=None, errbackKeywords=None):
"""Add a pair of callbacks (success and error) to this Deferred. These will be executed when the 'master' callback is run.
"""
assert callable(callback)
assert errback == None or callable(errback)
cbs = ((callback, callbackArgs, callbackKeywords),
(errback or (passthru), errbackArgs, errbackKeywords))
self.callbacks.append(cbs) if self.called:
self._runCallbacks()
return self def addCallback(self, callback, *args, **kw):
"""Convenience method for adding just a callback. See L{addCallbacks}.
""" return self.addCallbacks(callback, callbackArgs=args,
callbackKeywords=kw)
addCallback第二个参数是要添加的函数,第三个参数是要传递给回调函数的参数,第四个是要传递给回调函数
的关键字,然后,addCallback里调用了addCallbacks 。 我们可以看到,在deferred初始化 时,这对回调链self.callbacks 是空的
def __init__(self):
self.callbacks = []
if self.debug:
self._debugInfo = DebugInfo()
self._debugInfo.creator = traceback.format_stack()[:-1]
cbs = ((callback, callbackArgs, callbackKeywords),
(errback or (passthru), errbackArgs, errbackKeywords))
self.callbacks.append(cbs)
看addCallbacks源码,你会发现, 如果没有指定要添加的errback 时,errback 则是pass-through 的。这时,这对回调函数就会被添加到回调链的尾, 效果如图所示:
当调用一个回调函数时,deferred 又会怎么做呢?假设当前调用的是 callback:
def callback(self, result):
"""Run all success callbacks that have been added to this Deferred. Each callback will have its result passed as the first
argument to the next; this way, the callbacks act as a
'processing chain'. Also, if the success-callback returns a Failure
or raises an Exception, processing will continue on the *error*-
callback chain.
"""
assert not isinstance(result, Deferred)
self._startRunCallbacks(result)
看作者的注释,deferred 把上一个回调函数的结果result传递给 _startRunCallbacks:
def _startRunCallbacks(self, result):
if self.called:
if self.debug:
if self._debugInfo is None:
self._debugInfo = DebugInfo()
extra = "\n" + self._debugInfo._getDebugTracebacks()
raise AlreadyCalledError(extra)
raise AlreadyCalledError
if self.debug:
if self._debugInfo is None:
self._debugInfo = DebugInfo()
self._debugInfo.invoker = traceback.format_stack()[:-2]
self.called = True
self.result = result
if self.timeoutCall:
try:
self.timeoutCall.cancel()
except:
pass del self.timeoutCall
self._runCallbacks()
但这时还没有真正调用了我们刚才添加的回调函数,在这个_startRunCallbacks 里是找不到回调链
“self.callbacks ”的字眼的。这个函数是“开始执行回调函数”,对执行之前处理了一些事务,但它最后调用了
_runCallbacks:
def _runCallbacks(self):
if self._runningCallbacks:
# Don't recursively run callbacks
return
if not self.paused:
while self.callbacks:
item = self.callbacks.pop(0)
callback, args, kw = item[
isinstance(self.result, failure.Failure)]
args = args or ()
kw = kw or {} try:
self._runningCallbacks = True
try:
self.result = callback(self.result, *args, **kw)
finally:
self._runningCallbacks = False
if isinstance(self.result, Deferred):
# note: this will cause _runCallbacks to be called
# recursively if self.result already has a result.
# This shouldn't cause any problems, since there is no
# relevant state in this stack frame at this point.
# The recursive call will continue to process
# self.callbacks until it is empty, then return here,
# where there is no more work to be done, so this call
# will return as well.
self.pause()
self.result.addBoth(self._continue)
break
except:
self.result = failure.Failure() if isinstance(self.result, failure.Failure):
self.result.cleanFailure()
if self._debugInfo is None:
self._debugInfo = DebugInfo()
self._debugInfo.failResult = self.result
else:
if self._debugInfo is not None:
self._debugInfo.failResult = None
self.callbacks.pop(0) #弹出回调链里的第一对回调函数,
callback, args, kw = item[
isinstance(self.result, failure.Failure)] #根据self.result是否为 failure.Failure类型,来选择回调函数
self.result = callback(self.result, *args, **kw) #这才真正执行了回调函数并把执行结果 作为参数传递给下 #一个回调函数
通过作者的注释,我们可以看到,当执行完回调函数之后,
(1)若self.result 为deferred,即如果回调函数返回一个新的deferred,则当前的deferred(外部deferred)会
暂停下来,去执行新的deferred(内部deferred)里的回调函数,直到内部deferred把控制权转交给外部
deferred,如图:
看代码:
self.pause()
self.result.addBoth(self._continue)
而addBoth会调用addCallbacks, addCallbacks 则调用_runCallbacks,
def addBoth(self, callback, *args, **kw):
"""Convenience method for adding a single callable as both a callback
and an errback. See L{addCallbacks}.
"""
return self.addCallbacks(callback, callback,
callbackArgs=args, errbackArgs=args,
callbackKeywords=kw, errbackKeywords=kw) def addCallbacks(self, callback, errback=None,
callbackArgs=None, callbackKeywords=None,
errbackArgs=None, errbackKeywords=None):
"""Add a pair of callbacks (success and error) to this Deferred. These will be executed when the 'master' callback is run.
"""
assert callable(callback)
assert errback == None or callable(errback)
cbs = ((callback, callbackArgs, callbackKeywords),
(errback or (passthru), errbackArgs, errbackKeywords))
self.callbacks.append(cbs) if self.called:
self._runCallbacks()
return self
调用顺序为:
addBoth --> addCallbacks --> _runCallbacks, 如此递归执行。
(2)若self.result 不为deferred,则执行下一个回调函数,并把self.result 传递给下一个回调函数,直到self.result为 空。整个跟踪过程就到这里把!
参考:
http://twistedmatrix.com/trac/browser/tags/releases/twisted-.2.0/twisted/internet/defer.py
http://krondo.com/?page_id=1327
http://blog.sina.com.cn/s/blog_704b6af70100py9n.html
版权声明:本文为博主原创文章,未经博主允许不得转载。
跟踪 twisted 里deferred 的Callback的更多相关文章
- twisted之Deferred类的分析
@_oldStyle class Deferred: called = False#类变量,在实例中引用时会自动在实例中生成 paused = False _debugInfo = None _sup ...
- python网络编程——SocketServer/Twisted/paramiko模块
在之前博客C/S架构的网络编程中,IO多路复用是将多个IO操作复用到1个服务端进程中进行处理,即无论有多少个客户端进行连接请求,服务端始终只有1个进程对客户端进行响应,这样的好处是节省了系统开销(se ...
- Python Twisted系列教程9:第二个小插曲,Deferred
作者:dave@http://krondo.com/a-second-interlude-deferred/ 译者:杨晓伟(采用意译) 可以从这里从头来阅读这个系列 更多关于回调的知识 稍微停下来再思 ...
- Python Twisted系列教程8:使用Deferred的诗歌下载客户端
作者:dave@http://krondo.com/deferred-poetry/ 译者:杨晓伟(采用意译) 可以从这里从头开始阅读这个系列. 客户端4.0 我们已经对deferreds有些理解了 ...
- Python Twisted系列教程7:小插曲,Deferred
作者:dave@http://krondo.com/an-interlude-deferred/ 译者:杨晓伟(采用意译) 你可以从这里从头开始阅读这个系列 回调函数的后序发展 在第六部分我们认识这 ...
- [Twisted] deferred
Twisted提供一个优雅的实现(Deferred)来管理回调函数. Deferred Object 的结构 Deferred Object包含两个回调函数列表.一个用来保存成功的回调函数,另一个用来 ...
- Python Twisted系列教程13:使用Deferred新功能实现新客户端
作者:dave@http://krondo.com/deferred-all-the-way-down/ 译者:杨晓伟(采用意译) 你可以从这里从头阅读这个系列. 介绍 回忆下第10部分中的客户端5 ...
- 理解twisted中的reactor和deferred(二)
Deferred可以添加多个回调函数,每个回调函数的结果作为下一个回调函数的参数 代码实例(可在pycharm中运行,摘自 https://twistedmatrix.com/documents/cu ...
- Firefly distributed模块的原理与twisted中PB远程调用协议
这些天断断续续在看Firefly, 看了一下distributed模块的设计,其实就是使用的twisted.spread.pb觉得以后要是想用Firefly有必要了解一下twisted, 所以在网上查 ...
随机推荐
- Unix 环境高级编程
UNIX 环境高级编程 本书描述了UNIX系统的程序设计接口--系统调用接口和标准C库提供的很多函数. 与大多数操作系统一样,Unix为程序员运行提供了大量的服务--打开文件,读文件,启动一个新程序, ...
- 使用pycharm手动搭建python语言django开发环境(五) 使用日志模块打日志
1.在项目的settings.py中增加日志相关声明 #增加日志设置 LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'fil ...
- thinkphp No input file specified的解决方法
.htaccess RewriteRule ^(.*)$ index.php?s=$1 [QSA,PT,L]
- servlet 传值给 jsp
java package helloworld; import java.io.IOException; import javax.servlet.RequestDispatcher; import ...
- Spring4 MVC ContentNegotiatingViewResolver多种输出格式实例
本文演示支持多种输出格式,这里 Spring4 MVC应用程序使用了 Spring ContentNegotiatingViewResolver .我们将生成应用程序输出XML,JSON,PDF,XL ...
- WPF-Binding对数据的检验
设置Binding的ValidationRules属性对Binding进行检验 <StackPanel> <TextBox x:Name="txtAge" Fon ...
- Python Socket套接字
socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. socket起源于Un ...
- 更改Scala的代码自己主动补全快捷键code completion
使用Eclipse的代码补全快捷键alt+/已经习惯了.可是IntelJIDEA中这个快捷键组合没有效果,那么我们来改动之,按ctrl+alt+s打开设置界面 之后在keymaps中选择Eclipse ...
- jmeter 响应数据更换显示类型(json、html、text)
.默认情况下是Text格式 2.修改为json格式显示
- Android———最详细的系统对话框(AlertDialog)详解
在实际应用开发中,用到系统对话框中的情况几乎是没有的.按开发流程来说,UI工程师都会给出每一个弹窗的样式,故而在实际开发中都是自定义弹窗的. 即使用到的地方不多,但是我们也是需要了解并且能熟练的运用它 ...