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. iOS wkWebView点击链接无反应

    在使用wkWebView的时候发现用webView加载youku.com,里面的视频链接点击是可以跳转的,但是呢,加载v.qq.com的时候发现链接点击又没有效果,折腾了很久最后找到解决方案 实现WK ...

  2. jquery - 动态绑定事件

    举个例子: html页面 <div><button type="button" class="test">测试</button&g ...

  3. tomcat的bin目录中startup.bat/tomcat.6.exe/tomcat6w.exe区别

    一.tomcat6.exe 与 startup.bat的区别 1.两者都可以用于启动Tomcat tomcat6.exe则是必须将tomcat注册Windows服务之后才可以用于启动tomcat服务; ...

  4. Linux 文件管理(C语言库函数二--程序日志)

    文件删除和改名 int remove(const char *pathname); int rename(const char *oldpath,const char *newpath); remov ...

  5. PC如何访问手机网址

    http://blog.csdn.net/matthew_fan/article/details/7787504

  6. IDEA15入门常用设置

    打开IDEA设置的快捷键:Ctrl + Alt + S 打开选中的项目属性快捷键:Shift + Ctrl + Alt + S 1.IDEA默认不会使用我们独立安装的Maven配置,需要手动设置,并且 ...

  7. JavaScript API 设计准则

    好的 API 设计:在自描述的同时,达到抽象的目标. 设计良好的 API ,开发者可以快速上手,没必要经常抱着手册和文档,也没必要频繁光顾技术支持社区. 流畅的接口 方法链:流畅易读,更易理解 //常 ...

  8. 使用PHP函数输出前一天的时间和后一天的时间

    1.明确date()函数和time()函数的功能,其中time()函数是获取时间戳函数 2.输出前一天的当前时间: echo '一天之前的时间为:'.date('Y-m-d H:i:s',time() ...

  9. HttpWatch使用教程

    一 概述: HttpWatch强大的网页数据分析工具.集成在Internet Explorer工具栏.包括网页摘要.Cookies管理.缓存管理.消息头发送/接受.字符查询.POST 数据和目录管理功 ...

  10. 如何导入和导出应用数据通过电子邮件应用程序在你的IOS

     本文转载至 http://blog.csdn.net/zaitianaoxiang/article/details/6657887 http://yueding920.blog.163.com/bl ...