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. 配置HADOOP_HOME

    配置HADOOP_HOME export HADOOP_HOME=/usr/hadoop-1.2.0export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sb ...

  2. Office Web Apps Server 2013与PDF(一)

    好吧--这个消息有点旧,迟了将近4个月. Office Web Apps是微软各大服务产品系列中的一个基础服务,可以为SharePoint 2013.Exchange 2013.Lync 2013提供 ...

  3. 你设计的应用是IO依赖型还是CPU依赖型?

    这个话题可能看起来非常枯燥,但它对mysql的性能优化非常重要. 其实我在MYSQL 咨询工作中无时无刻接不在接触这类问题. IO工作负载与cpu依赖全然不同,尤其是当你的工作集(通常仅仅有数据库的一 ...

  4. echarts(3.0)的基本使用(标签式导入)

    function loadRainFallCharts(msg) { var obj = {}; obj.x = []; obj.y = []; obj.line = []; var accumula ...

  5. EntityFramework(EF) 单表与主从表的使用

    一.单表Reader 1 构建Reader类 public   class Reader    {       public int ReaderID { get; set; }       publ ...

  6. 数值孔径NA、分辨率极限与衍射极限

    一.数值孔径  数值孔径(NA):是一个无量纲的数,用以衡量该系统能够收集的光的角度范围.越大,收集到的光越多,分辨率越高. 描述了透镜收光锥角的大小,决定着透镜收光能力和空间分辨率. 数值孔径(NA ...

  7. Laravel 5.1 数组帮助函数(随发现更新)

    array_add 向一个数组中添加指定键值,如果键值不存在则添加,如果键本身就存在 那么就不添加: $test_array = ['name' => '大K', 'age' => 27] ...

  8. Android无线测试之—UiAutomator UiDevice API介绍四

    拖拽与滑动 一.概念介绍: 1)拖拽:将组建从一个坐标移动到另一个坐标 2)移动:从一二坐标点移动到另一个坐标点 3)步长:从一点滑动到另一点使用的时间 二.拖拽与滑动的相关API: 返回值 方法名 ...

  9. CMU-15445 LAB3:事务隔离,two-phase locking,锁管理器

    概述 本lab将实现一个锁管理器,事务通过锁管理器获取锁,事务管理器根据情况决定是否授予锁,或是阻塞等待其它事务释放该锁. 背景 事务属性 众所周知,事务具有如下属性: 原子性:事务要么执行完成,要么 ...

  10. 关于jquery的取消阻止默认事件

    最近帮朋友做个东西,整个屏幕有一个遮罩层,但是这个遮罩层能滑动,于是百度了一下,返现,用 $('body').bind("touchmove",function(e){ e.pre ...