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的更多相关文章

  1. twisted之Deferred类的分析

    @_oldStyle class Deferred: called = False#类变量,在实例中引用时会自动在实例中生成 paused = False _debugInfo = None _sup ...

  2. python网络编程——SocketServer/Twisted/paramiko模块

    在之前博客C/S架构的网络编程中,IO多路复用是将多个IO操作复用到1个服务端进程中进行处理,即无论有多少个客户端进行连接请求,服务端始终只有1个进程对客户端进行响应,这样的好处是节省了系统开销(se ...

  3. Python Twisted系列教程9:第二个小插曲,Deferred

    作者:dave@http://krondo.com/a-second-interlude-deferred/ 译者:杨晓伟(采用意译) 可以从这里从头来阅读这个系列 更多关于回调的知识 稍微停下来再思 ...

  4. Python Twisted系列教程8:使用Deferred的诗歌下载客户端

    作者:dave@http://krondo.com/deferred-poetry/  译者:杨晓伟(采用意译) 可以从这里从头开始阅读这个系列. 客户端4.0 我们已经对deferreds有些理解了 ...

  5. Python Twisted系列教程7:小插曲,Deferred

    作者:dave@http://krondo.com/an-interlude-deferred/  译者:杨晓伟(采用意译) 你可以从这里从头开始阅读这个系列 回调函数的后序发展 在第六部分我们认识这 ...

  6. [Twisted] deferred

    Twisted提供一个优雅的实现(Deferred)来管理回调函数. Deferred Object 的结构 Deferred Object包含两个回调函数列表.一个用来保存成功的回调函数,另一个用来 ...

  7. Python Twisted系列教程13:使用Deferred新功能实现新客户端

    作者:dave@http://krondo.com/deferred-all-the-way-down/  译者:杨晓伟(采用意译) 你可以从这里从头阅读这个系列. 介绍 回忆下第10部分中的客户端5 ...

  8. 理解twisted中的reactor和deferred(二)

    Deferred可以添加多个回调函数,每个回调函数的结果作为下一个回调函数的参数 代码实例(可在pycharm中运行,摘自 https://twistedmatrix.com/documents/cu ...

  9. Firefly distributed模块的原理与twisted中PB远程调用协议

    这些天断断续续在看Firefly, 看了一下distributed模块的设计,其实就是使用的twisted.spread.pb觉得以后要是想用Firefly有必要了解一下twisted, 所以在网上查 ...

随机推荐

  1. 5 月 35 日临近,Google 无法访问,可以使用 Google IP 来解决。

    每年都会有几天那啥,你懂的. 直接使用 Google 的域名访问经常会打不开,而使用 Google 的 IP 就会很顺畅. 使用 Chrome 浏览器我们经常都会在地址栏直接搜索,所以我们只要添加一个 ...

  2. IOS开发之ZBarReaderView的使用

    IOS开发之ZBarReaderView的使用 HOMEABOUTGUESTBOOKCATEGORIESTAGSLINKSSUBSCRIBE 当开发IOS程序中需要用到二维码识别功能的时候,zbar这 ...

  3. Java基础02 方法与数据成员(转载)

    对象中的数据成员表示对象的状态.对象可以执行方法,表示特定的动作. 此外,我们还了解了类(class).同一类的对象属于相同的类型(type).我们可以定义类,并使用该定义来产生对象.   调用同一对 ...

  4. php 字符串截取

    $str="3,22,11,444,33,1,3455,33,22,444,55,66,77,88,99,554336,"; echo substr($str,0,strlen($ ...

  5. 【转】锤子CTO钱晨:福利好是一种堕落的公司文化

    “这是拉勾网对锤子科技CTO钱晨的访谈,作为中国手机界三大产品经理之一,他带领着一众硬件工程师在手机红海中厮杀.钱晨喜欢焦虑的工程师,佩服有方向感的人. 本文作者:西岳 拉勾网原创出品,转载请注明作者 ...

  6. Spring4 MVC REST服务使用@RestController实例

    在这篇文章中,我们将通过开发使用 Spring4 @RestController 注解来开发基于Spring MVC4的REST风格的JSON服务.我们将扩展这个例子通过简单的注释与JAXB标注域类支 ...

  7. 图像增强之DDE---基于红外图像的数字图像细节增强DDE

    (1)DDE应用背景 (2)DDE算法简介 (3)DDE 实现 (4)DDE 总结和不足 ----------author:pkf -----------------time:2-9 -------- ...

  8. Python简单的线程池

    class ThreadPool(object): def __init__(self, max_num=20): # 创建一个队列,队列里最多只能有10个数据 self.queue = queue. ...

  9. tomcat NIO配置

    1.tomcat NIO配置 今天在查看日志时发现tomcat的Socket连接方式为bio,于是我想既然有bio那肯定有nio.果然,一查就发现tomcat在6.0之后就可以配置nio的方式.nio ...

  10. 如何停止和扭转UIView的动画

    本文转载至  http://codego.net/576089/ 我有它收缩时碰到切换按钮UIView的动画跳和它扩展恢复到原来的大小当再次接触到按钮.密封式前大灯一切都工作得很好.问题是,动画师注意 ...