在《Learning Scrapy》第225页看到这一段:
>>> # Experiment 3
>>> def status(*ds):
... return [(getattr(d, 'result', "N/A"), len(d.callbacks)) for d in
ds]
>>> def b_callback(arg):
... print "b_callback called with arg =", arg
... return b
>>> def on_done(arg):
... print "on_done called with arg =", arg
... return arg
>>> # Experiment 3.a
>>> a = defer.Deferred()
>>> b = defer.Deferred()
>>> a.addCallback(b_callback).addCallback(on_done)
>>> status(a, b)
[('N/A', 2), ('N/A', 0)]
>>> a.callback(3)
b_callback called with arg = 3
>>> status(a, b)
[(<Deferred at 0x10e7209e0>, 1), ('N/A', 1)]
>>> b.callback(4)
on_done called with arg = 4
>>> status(a, b)
[(4, 0), (None, 0)]
对于种结果百思不得其解,最后从源码注释中找到了答案:
line 444:
If a callback (or errback) returns another L{Deferred}, this L{Deferred} will be chained to it (and further callbacks will not run until that L{Deferred} has a result).
执行过程从第668行开始:
if isinstance(current.result, Deferred):
# The result is another Deferred. If it has a result,
# we can take it and keep going.
resultResult = getattr(current.result, 'result', _NO_RESULT)
if resultResult is _NO_RESULT or isinstance(resultResult, Deferred) or current.result.paused:
# Nope, it didn't. Pause and chain.
current.pause()
current._chainedTo = current.result
# Note: current.result has no result, so it's not
# running its callbacks right now. Therefore we can
# append to the callbacks list directly instead of
# using addCallbacks.
current.result.callbacks.append(current._continuation())
break
这就解释了为何运行到b.callback(4)时,a的状态包含“waiting for Deferred”。此时a处于pause状态。
那么后续的这一段也能理解了:
>>> # Experiment 3.b
>>> a = defer.Deferred()
>>> b = defer.Deferred()
>>> a.addCallback(b_callback).addCallback(on_done)
>>> status(a, b)
[('N/A', 2), ('N/A', 0)]
>>> b.callback(4)
>>> status(a, b)
[('N/A', 2), (4, 0)]
>>> a.callback(3)
b_callback called with arg = 3
on_done called with arg = 4
>>> status(a, b)
[(4, 0), (None, 0)]
执行callback
后,依次调用_startRunCallbacks
和_runCallbacks
方法。在_startRunCallbacks
中,self.result先被赋值为4,而在_runCallbacks
中,“while current.callbacks:
”包裹的循环会进一步影响self.result的值。
所以callback chain为[]时,也能调用callback并产生result。