Twisted中奇怪的回调执行顺序?

3 投票
1 回答
536 浏览
提问于 2025-04-17 06:48

考虑以下代码:

from twisted.internet.defer import Deferred

d1 = Deferred()
d2 = Deferred()

def f1(result):
    print 'f1',

def f2(result):
    print 'f2',

def f3(result):
    print 'f3',

def fd(result):
    return d2

d1.addCallback(f1)
d1.addCallback(fd)
d1.addCallback(f3)

#/BLOCK====
d2.addCallback(f2)
d1.callback(None)
#=======BLOCK/

d2.callback(None)

这个输出结果是我预期的:

f1 f2 f3

但是当我把代码块中的语句顺序调换成:

#/BLOCK====
d1.callback(None)
d2.addCallback(f2)
#=======BLOCK/

也就是说,在给d2添加回调之前先触发d1,我得到的结果是:

f1 f3 f2

我不明白为什么触发这些延迟操作的时间会影响回调的执行顺序。
这是Twisted的问题,还是说这样理解是有道理的?

1 个回答

3

总结一下 — 当你从一个回调函数(fd)返回一个延迟对象(d2)时,它会被插入到调用了这个回调的另一个延迟对象(d1)的回调链中。这个过程是通过将d1的回调链的后续部分作为回调添加到d2上来实现的,所以如果你在触发d1之后再给d2添加回调,它会被加在d1的后面。


我觉得你可能忽略了一个事实,那就是延迟对象本身并不是异步的,它们只是以一种更有结构的方式来链接异步代码中的回调和错误处理。回调并不是在某个不确定的时间被调用,而是在d2被触发的时候被调用。如果你想让它在更晚的时候执行,你需要把你的调用和一个reactor结合起来。

所以这可能让这个问题的答案变得更明显:

我不明白为什么延迟对象触发的时间会影响回调的执行顺序。

如果你在触发延迟对象之后更改回调,那就会影响。

你的代码解释

在第一个例子中:

  1. d1.callback(None) 触发了 d1
  2. f1 被调用(打印 "f1")
  3. fd 被调用(返回 d2

现在事情停下来了,因为d2已经被插入到d1的回调链中,在f1f3之间,但还没有被触发。然后...

  1. d2.callback(None) 触发了 d2
  2. f2 被调用(打印 "f2")
  3. d1 的回调链继续执行;所以 f3 被调用(打印 "f3")

在第二个例子中

  1. d1.callback(None) 触发了 d1
  2. f1 被调用(打印 "f1")
  3. fd 被调用(返回 d2

这里d2再次被插入到回调链中。这个过程是通过将当前回调链的后续部分添加为d2的回调来实现的。所以即使你明确地将f2作为回调添加到d2上,它也是在d1的回调链的后续部分之后被添加的。因此...

  1. d2.callback(None) 触发了 d2;这导致d1的回调链继续执行,从...
  2. f3 被调用(打印 "f3")
  3. d2 的下一个回调在它的链中是 f2,所以...
  4. f2 被调用(打印 "f2")

撰写回答