Twisted tutorial中提到了这种模式作为避免两次延迟相同的标准方法:
class A(ClientFactory):
...
def finished(self, result):
if self.deferred is not None:
d, self.deferred = self.deferred, None
d.callback(result)
但根据我的理解,Deferred不会让你(即如果你这样做会引发异常)两次调用同一个实例。那么为什么要在任何地方重复这个代码来重新创建这种安全机制呢?
答案 0 :(得分:1)
正如您所说,第二次拨打Deferred.callback
会提出AlreadyCalledError
。但是编写不会触发此异常的代码是惯用的。我编写或读取的大多数使用延迟的代码都会尝试保留AlreadyCalledError
来表示编程错误,而不是可以安全忽略的正常运行时条件。
所以:
def finished(self, result):
try:
self.deferred.callback(result)
except AlreadyCalledError:
pass
不是首选拼写。我认为这是因为可能还有其他原因引发AlreadyCalledError
,而不仅仅是因为finished
被多次调用而且这种错误处理会掩盖这些情况,可能会隐藏错误。< / p>
抛弃Deferred
实例的第二个原因是帮助意外异常日志记录。当Deferred
被垃圾收集并且它有一个未处理的Failure
结果时,会记录此Failure
。这通常表示编程错误。 Deferred
只要有任何引用就不能进行垃圾收集,所以在工厂丢弃引用至少可以确保工厂不会保持Deferred
活着(虽然使用Deferred
的应用程序代码仍然可以。)
另一种方法是写下这个:
def finished(self, result):
if self.deferred is not None:
self.deferred.callback(result)
self.deferred = None
这仍然会丢弃Deferred
但没有元组拆包。但是,这里有一个问题。
考虑更完整版的A
:
class A(ClientFactory):
def waitUntilFinished(self):
self.deferred = Deferred()
return self.deferred
def finished(self, result):
if self.deferred is not None:
d, self.deferred = self.deferred, None
d.callback(result)
正如您所看到的,在A.waitUntilFinished
发生一次之前调用finished
两次是不安全的。也就是说,如果你写:
a = A()
x = a.waitUntilFinished()
y = a.waitUntilFinished()
然后你可以非常肯定x
永远不会收到结果。这可能很难过,但您可以合理地将其记录为API的限制。
现在,请考虑这种略有不同的使用模式:
a = A()
x = a.waitUntilFinished()
def doSomething(result):
return a.waitUntilFinished()
x.addCallback(doSomething)
此代码在调用waitUntilFinished
之前不再调用finished
两次。在第二次调用finished
之前,它会等到waitUntilFinished
被调用。如果您将API记录为只能安全地调用一次,直到产生的延迟触发,有人可能会认为这种用法是合理的。
更简单的实施:
def finished(self, result):
if self.deferred is not None:
self.deferred.callback(result)
self.deferred = None
有问题。 doSomething
会调用self.deferred.callback(result)
。更具体地说,doSomething
在self.deferred.callback
返回之前被调用(换句话说,doSomething
由该语句同步调用)。 doSomething
调用waitUntilFinished
来创建新的Deferred
并将其分配给工厂的deferred
属性。然后self.deferred.callback(result)
结束并且self.deferred =无runs - and that new
延迟`被抛弃,永远不会被调用。
通过在调用其回调链之前将self.deferred
设置为None
,可以避免这种情况。