使用延迟机制(Using Deferreds)

-- -- Jerry Marx 于 [2004-08-23 04:35:55] 最后编辑

1. 介绍(Introduction)

Twisted is a framework that allows programmers to develop asynchronous networked programs. twisted.internet.defer.Deferred objects are one of the key concepts that you should understand in order to develop asynchronous code that uses the Twisted framework: they are a signal to the calling function that a result is pending.

Twisted是一个运行程序员以异步方式进行网络编程的框架.如果要理解Twisted框架并用它来开发异步程序"twisted.internet.defer.Deferred"是一个非常关键的概念:它给了调用函数一个操作未决的信号

This HOWTO first describes the problem that Deferreds solve: that of managing tasks that are waiting for data without blocking. It then illustrates the difference between blocking Python code, and non-blocking code which returns a Deferred; describes Deferreds in more details; describes the details of the class interfaces; and finally describes DeferredList.

这份HOWTO首先解释Deferreds要解决什么问题:它以不阻塞的方式管理等待数据的任务.然后举例说明返回Defrred的非阻塞代码和阻塞Python代码的区别;描述了Deferreds的更多细节;详细讨论类接口;最后描述DeferredList

2. Defferreds解决什么问题(The Problem that Deferreds Solve)

Deferreds are designed to enable Twisted programs to wait for data without hanging until that data arrives.

Deferreds被设计为一种可以让程序等待数据而不用在数据到来之前一直挂起的方式.

Many computing tasks take some time to complete, and there are two reasons why a task might take some time:

很多计算任务需要很长时间才能完成,下面列出两个可能的原因

  1. it is computationally intensive (for example factorising large numbers) and requires a certain amount of CPU time to calculate the answer; or
  2. it is not computationally intensive but has to wait for data to be available to produce a result.
  3. 可能是高强度计算(比方说找很大数字的因子)需要大量CPU时间来计算结果;或者
  4. 虽然不是高强度计算但是需要等待数据变得有效以产生结果

It is the second class of problem — non-computationally intensive tasks that involve an appreciable delay — that Deferreds are designed to help solve. Functions that wait on hard drive access, database access, and network access all fall into this class, although the time delay varies.

第二种问题--不是高强度计算,引起一个可感知的延迟--就是Deferreds要解决的问题.函数等待硬盘访问,等待数据库访问,等待网络访问都是这类问题,尽管等待的时间各不相同.

The basic idea behind Deferreds, and other solutions to this problem, is to keep the CPU as active as possible. If one task is waiting on data, rather than have the CPU (and the program!) idle waiting for that data (a process normally called "blocking"), the program performs other operations in the meantime, and waits for some signal that data is ready to be processed before returning to that process.

Deferreds和其他解决这个问题的方法的基本思想就是尽可能的让CPU可用.如果一个任务正在等待数据,就应该在等待数据到达的信号的同时进行另一个任务,而不是让CPU(和程序!)空闲等待数据(对于进程来说一般叫做"阻塞").

In Twisted, a function signals to the calling function that it is waiting by returning a Deferred. When the data is available, the program activates the callbacks on that Deferred to process the data.

在Twisted中,函数发信号给调用函数等待返回的Deferred(这句怎么都译不通 :( ).当数据可用的时候,程序调用注册到Deferred的回调函数来处理数据.

3. 今生前世(The Context)

3.1. 用阻塞来解决(Dealing with Blocking Code)

When coding I/O based programs - networking code, databases, file access - there are many APIs that are blocking, and many methods where the common idiom is to block until a result is gotten.

在编写基于I/O的程序--网络,数据库,文件访问--的时候,会用到很多会阻塞的API函数,很多方法的惯用法就是阻塞等待.

   1 class Getter:
   2     def getData(self, x):
   3         # imagine I/O blocking code here
   4         print "blocking"
   5         import time
   6         time.sleep(4)
   7         return x * 3
   8 
   9 g = Getter()
  10 print g.getData(3)

3.2. 不要打电话给我,我会打给你的(Don't Call Us, We'll Call You)

Twisted cannot support blocking calls in most of its code, since it is single threaded, and event based. The solution for this issue is to refactor the code, so that instead of blocking until data is available, we return immediately, and use a callback to notify the requester once the data eventually arrives.

由于Twisted以事件驱动的单线程方式工作,所以其大部分代码都不支持阻塞调用.解决这个问题的方法就是重构,我们直接返回而不是阻塞等待数据到达,然后在数据到达候使用回调函数机制通知请求者.

   1 from twisted.internet import reactor
   2 
   3 class Getter:
   4     def getData(self, x, callback):
   5         # this won't block
   6         reactor.callLater(2, callback, x * 3)
   7 
   8 def printData(d):
   9     print d
  10 
  11 g = Getter()
  12 g.getData(3, printData)
  13 
  14 # startup the event loop, exiting after 4 seconds
  15 reactor.callLater(4, reactor.stop);
  16 reactor.run()

There are several things missing in this simple example. There is no way to know if the data never comes back; no mechanism for handling errors. The example does not handle multiple callback functions, nor does it give a method to merge arguments before and after execution. Further, there is no way to distinguish between different calls to gotData from different producer objects. Deferred solves these problems, by creating a single, unified way to handle callbacks and errors from deferred execution.

这个例子缺少了一些东西.没有办法知道数据是否永远不会到达,没有错误处理机制.这个例子也不能处理多个回调函数,也不能提供一个函数在执行前或执行后合并参数.更进一步,它不能分辨从不同数据源获取的不同的数据.Deferred提供了一种简单,统一的方法来处理回调和错误从而可以解决上面的所有问题.

4. 延迟机制(Deferreds)

A "twisted.internet.defer.Deferred" is a promise that a function will at some point have a result. We can attach callback functions to a Deferred, and once it gets a result these callbacks will be called. In addition Deferreds allow the developer to register a callback for an error, with the default behavior of logging the error. The deferred mechanism standardizes the application programmer's interface with all sorts of blocking or delayed operations.

一个"twisted.internet.defer.Deferred"许诺一个函数在某一点会返回结果.我们可以把一些回调函数注册到Deferred,一旦有结果这些回调函数就会被调用.此外Deferred也允许开发者注册对于错误处理的回调函数取代缺省的写入日值的错误处理行为.Deferred机制提供了处理各种阻塞和延迟的一种标准化的接口.

   1 from twisted.internet import reactor, defer
   2 
   3 class Getter:
   4     def getData(self, x):
   5         # this won't block
   6         d = defer.Deferred()
   7         reactor.callLater(2, d.callback, x * 3)
   8         return d
   9 
  10 def printData(d):
  11     print d
  12 
  13 g = Getter()
  14 d = g.getData(3)
  15 d.addCallback(printData)
  16 
  17 reactor.callLater(4, reactor.stop); reactor.run()

Deferreds do not make the code magically not block. Once you have rewritten your code to not block, Deferreds give you a nice way to build an interface to that code.

Defrreds并不是对代码施加了什么魔力使得它们不会阻塞.一旦你以非阻塞的方式重写你的代码,Deferreds可以给你创建这样接口的一种非常好的方法.

As we said, multiple callbacks can be added to a Deferred. The first callback in the Deferred's callback chain will be called with the result, the second with the result of the first callback, and so on. Why do we need this? Well, consider a Deferred returned by twisted.enterprise.adbapi - the result of a SQL query. A web widget might add a callback that converts this result into HTML, and pass the Deferred onwards, where the callback will be used by twisted to return the result to the HTTP client. The callback chain will be bypassed in case of errors or exceptions.

我们前面提到,可以注册多个回调到一个Deferred对象.Deferred的回调函数链中的第一个函数调用时会收到Deferred返回的结果,第二个回调函数被调用的时候收到第一个回调函数返回的结果,依此类推.为什么我们需要这样作?嗯,假设一个Deferred对象返回了twisted.enterprise.adbapi - SQL查询的结果.一个Web部件可以加入一个回调把这个结果转换为HTML,然后继续传给Deferred,下一个函数会把这个结果返回给HTTP客户.回调函数链在错误或者异常发生的时候会通过旁路返回.

   1 from twisted.internet import reactor, defer
   2 
   3 class Getter:
   4     def gotResults(self, x):
   5         """The Deferred mechanism provides a mechanism to signal error
   6            conditions.  In this case, odd numbers are bad.
   7         """
   8         if x % 2 == 0:
   9             self.d.callback(x*3)
  10         else:
  11             self.d.errback(ValueError("You used an odd number!"))
  12 
  13     def _toHTML(self, r):
  14         return "Result: %s" % r
  15 
  16     def getData(self, x):
  17         """The Deferred mechanism allows for chained callbacks.
  18            In this example, the output of gotResults is first
  19            passed through _toHTML on its way to printData.
  20         """
  21         self.d = defer.Deferred()
  22         reactor.callLater(2, self.gotResults, x)
  23         self.d.addCallback(self._toHTML)
  24         return self.d
  25 
  26 def printData(d):
  27     print d
  28 
  29 def printError(failure):
  30     import sys
  31     sys.stderr.write(str(failure))
  32 
  33 # this will print an error message
  34 g = Getter()
  35 d = g.getData(3)
  36 d.addCallback(printData)
  37 d.addErrback(printError)
  38 
  39 # this will print "Result: 12"
  40 g = Getter()
  41 d = g.getData(4)
  42 d.addCallback(printData)
  43 d.addErrback(printError)
  44 
  45 reactor.callLater(4, reactor.stop); reactor.run()

4.1. 形象解释(Visual Explanation)

<<<图1>>>

  1. Requesting method (data sink) requests data, gets Deferred object.
  2. Requesting method attaches callbacks to Deferred object.
  3. 请求数据(从数据接收器),得到Deferred对象
  4. 注册回调到Deferred对象.

<<<图2>>>

When the result is ready, give it to the Deferred object. .callback(result) if the operation succeeded, .errback(failure) if it failed. Note that failure is typically an instance of a twisted.python.failure.Failure instance. Deferred object triggers previously-added (call/err)back with the result or failure. Execution then follows the following rules, going down the chain of callbacks to be processed. Result of the callback is always passed as the first argument to the next callback, creating a chain of processors. If a callback raises an exception, switch to errback. An unhandled failure gets passed down the line of errbacks, this creating an asynchronous analog to a series to a series of except: statements. If an errback doesn't raise an exception or return a twisted.python.failure.Failure instance, switch to callback.

当数据抵达就返回给Deferred对象. 操作成功就调用.callback(result),失败就调用.errback(failure).failure是一个"twisted.python.failure.Failure"实例 Deffered对象触发先前加入的回调/错误回调函数,传给result或failure.回调函数链中的函数会依次被执行. 如果回调过程中发生异常,就切换到错误回调中去执行. 一个未处理错误会依次调用错误回调函数链中的函数,创建一系列的和异常相似的异步相似物:状态. 如果异常回调函数不继续抛出异常或者返回"twisted.python.failure.Failure"实例,就切换到回调函数链执行.

4.2. 关于回调(More about callbacks)

注册多个回调函数到Deferred:

   1 g = Getter()
   2 d = g.getResult(3)
   3 d.addCallback(processResult)
   4 d.addCallback(printResult)

Each callback feeds its return value into the next callback (callbacks will be called in the order you add them). Thus in the previous example, processResult's return value will be passed to printResult, instead of the value initially passed into the callback. This gives you a flexible way to chain results together, possibly modifying values along the way (for example, you may wish to pre-process database query results).

每个回调函数都把自己的返回值传给下一个回调函数(回调函数会按照注册的顺序被调用).在上门的例子中,processResult()的返回值会传给printResult(),而不是最初传给processResult()那个值.这样就可以灵活的组织回调函数链,可以在这个链的传递过程中修改数据(例如可以对数据库查询结果预处理).

4.3. 关于出错回调(More about errbacks)

Deferred's error handling is modeled after Python's exception handling. In the case that no errors occur, all the callbacks run, one after the other, as described above.

Deferred的错误处理是对Python异常处理的模拟.没有错误发生的情况下所有的回调函数(callback)会一个接一个的被调用,如上所述.

If the errback is called instead of the callback (e.g. because a DB query raised an error), then a twisted.python.failure.Failure is passed into the first errback (you can add multiple errbacks, just like with callbacks). You can think of your errbacks as being like except blocks of ordinary Python code.

如果调用了错误回调函数(errback)而不是回调函数(callback)(例如因为一个数据库查询异常),一个twisted.python.failure.Failure对象就会传给第一个错误回调函数(errback)(你可以定义多个错误回调函数,就像回调函数一样).你可以认为你的错误回调就像是原生Python代码的except块.

Unless you explicitly raise an error in except block, the Exception is caught and stops propagating, and normal execution continues. The same thing happens with errbacks: unless you explicitly return a Failure or (re-)raise an exception, the error stops propagating, and normal callbacks continue executing from that point (using the value returned from the errback). If the errback does returns a Failure or raise an exception, then that is passed to the next errback, and so on.

除非你显式的在except块抛出(raise)一个错误,错误会被捕捉而不是继续传播,正常的执行会继续.错误回调(errback)也是这样:除非你显式的返回(return)一个Failure或者(重新)抛出一个异常,错误不会继续传播,正常的回调函数会在这点开始继续执行(使用错误回调的返回值).如果错误回调确实返回一个Failure或者抛出一个异常,下一个错误回调就会被调用,依此类推.

Note: If an errback doesn't return anything, then it effectively returns None, meaning that callbacks will continue to be executed after this errback. This may not be what you expect to happen, so be careful. Make sure your errbacks return a Failure (probably the one that was passed to it), or a meaningful return value for the next callback.

注意:如果错误回调没有返回任何值,它就返回None,意味着在这个错误回调之后正常回调序列会继续执行.这也许不是你想要的,所以要小心.确定你在错误回调中返回了一个Failure(很可能就是别的函数传给它的那个),或者返回有意义的值给下一个正常回调函数(callback).

Also, twisted.python.failure.Failure instances have a useful method called trap, allowing you to effectively do the equivalent of:

此外.twisted.python.failure.Failure有一个非常有用的方法叫做陷阱(trap),你可以用它有效的做相同的事情:

   1 try:
   2     # code that may throw an exception
   3     cookSpamAndEggs()
   4 except (SpamException, EggException):
   5     # Handle SpamExceptions and EggExceptions
   6     ...

You do this by:

你可以这样做:

   1 def errorHandler(failure):
   2     failure.trap(SpamException, EggException)
   3     # Handle SpamExceptions and EggExceptions
   4 
   5 d.addCallback(cookSpamAndEggs)
   6 d.addErrback(errorHandler)

If none of arguments passed to failure.trap match the error encapsulated in that Failure, then it re-raises the error.

如果传给failure.trap()的任何一个参数都不能匹配Failure包装的错误,这个错误就会被重新抛出.

There's another potential gotcha here. There's a method twisted.internet.defer.Deferred.addCallbacks which is similar to, but not exactly the same as, addCallback followed by addErrback. In particular, consider these two cases:

这是另一个潜在的陷阱.这里有个类似的但又不完全相同的方法twisted.internet.defer.Deferred.addCallbacks就是,在addErrBack()之后调用addCallBack().特别的,考虑下面两个例子:

   1 # Case 1
   2 d = getDeferredFromSomewhere()
   3 d.addCallback(callback1)       # A
   4 d.addErrback(errback1)         # B
   5 d.addCallback(callback2)
   6 d.addErrback(errback2)
   7 
   8 # Case 2
   9 d = getDeferredFromSomewhere()
  10 d.addCallbacks(callback1, errback1)  # C
  11 d.addCallbacks(callback2, errback2)

If an error occurs in callback1, then for Case 1 errback1 will be called with the failure. For Case 2, errback2 will be called. Be careful with your callbacks and errbacks.

如果在callback1()中发生了错误,Case 1中的errback1()会被传入failure来调用.在Case 2中,errback2()会被调用.运用callback和errback的时候千万要小心.

What this means in a practical sense is in Case 1, "A" will handle a success condition from getDeferredFromSomewhere, and "B" will handle any errors that occur from either the upstream source, or that occur in 'A'. In Case 2, "C"'s errback1 will only handle an error condition raised by getDeferredFromSomewhere, it will not do any handling of errors raised in callback1.

在Case 1中代码到底意思是什么呢?"A"会处理getDeferredFromSomeWhere()的成功情况,"B"会处理任何之前发生的错误,或者在"A"中发生的错误.在Case 2中,"C"的errback1只会处理getDeferredFromSomeWhere()中发生的错误,它不会处理任何callback1中抛出的错误.

4.4. 未处理的错误(Unhandled Errors)

If a Deferred is garbage-collected with an unhandled error (i.e. it would call the next errback if there was one), then Twisted will write the error's traceback to the log file. This means that you can typically get away with not adding errbacks and still get errors logged. Be careful though; if you keep a reference to the Deferred around, preventing it from being garbage-collected, then you may never see the error (and your callbacks will mysteriously seem to have never been called). If unsure, you should explicitly add an errback after your callbacks, even if all you do is:

如果Deferred在垃圾回收的时候遇到了一个未处理的错误(也就是说如果有下一个errback的话就会被调用),Twisted会把错误跟踪信息写到日志文件.这意味着你可以不用添加errback就拥有错误日志.要小心的是,如果你保留了Deferred的一个引用而阻止它被回收的话,你可能永远都看不到错误(并且你的callback也看似神秘的永远不会被调用).如果你不是很确定的话,就应该显式的在callback后面添加一个errback,如下所示:

# Make sure errors get logged
from twisted.python import log
d.addErrback(log.err)

5. 类概述(Class Overview)

This is the overview API reference for Deferred. It is not meant to be a substitute for the docstrings in the Deferred class, but can provide guidelines for its use.

这里是浏览一下Deferred的API参考.我们的目的并不是要取代Deferred类的docstring,只是提供一个使用的指导.

5.1. 基本回调函数(Basic Callback Functions)

  • addCallbacks(self, callback[, errback, callbackArgs, errbackArgs, errbackKeywords, asDefaults])
    • This is the method you will use to interact with Deferred. It adds a pair of callbacks parallel to each other (see diagram above) in the list of callbacks made when the Deferred is called back to. The signature of a method added using addCallbacks should be myMethod(result, *methodArgs, **methodKeywords). If your method is passed in the callback slot, for example, all arguments in the tuple callbackArgs will be passed as *methodArgs to your method. 这是你用来和Deferred交互的方法.它添加了一对callback到平行的两条(参见上面的示意图)回调处理链中.使用addCallbacks添加的函数的签名式应该是myMethod(result, *methodArgs, **methodKeywords).举个例子:如果你的方法传给了callback槽,在元组callbackArgs中的所有参数都会作为*meghodArgs传给你的方法. There are various convenience methods that are derivative of addCallbacks. I will not cover them in detail here, but it is important to know about them in order to create concise code. 还有一些派生自addCallbacks的方便的方法.在这里我不会列出所有细节,但是如果想要写出简练的代码的话知道下面这些是很重要的.
      • addCallback(callback, *callbackArgs, **callbackKeywords)
        • Adds your callback at the next point in the processing chain, while adding an errback that will re-raise its first argument, not affecting further processing in the error case. 将你的callback天加到处理链的下一点,在添加errback的时候将会重新抛出它的第一个参数,但在错误情况下不会影响未来的处理. Note that, while addCallbacks (plural) requires the arguments to be passed in a tuple, addCallback (singular) takes all its remaining arguments as things to be passed to the callback function. The reason is obvious: addCallbacks (plural) cannot tell whether the arguments are meant for the callback or the errback, so they must be specifically marked by putting them into a tuple. addCallback (singular) knows that everything is destined to go to the callback, so it can use Python's * and ** syntax to collect the remaining arguments. 注意:addCallbacks(复数)要求传入参数以元组(tuple)的方式,addCallback(单数)要求所有要传给callback函数的参数.原因是显而易见的:addCallbacks(复数)不能分辨参数是传给callback的还是errback的,所以它们必须以元组的方式来组织,而addCallback(单数)知道要传给callback的梭鱼东西,所以它使用了Python的*和**的语法形式来获得参数.
      • addErrback(errback, *errbackArgs, **errbackKeywords)
        • Adds your errback at the next point in the processing chain, while adding a callback that will return its first argument, not affecting further processing in the success case. 将你的errback添加到处理链的下一点,在添加callback的时候将会返回它的第一个参数,对于在成功的情况下不影响未来的处理
      • addBoth(callbackOrErrback, *callbackOrErrbackArgs, **callbackOrErrbackKeywords)
        • This method adds the same callback into both sides of the processing chain at both points. Keep in mind that the type of the first argument is indeterminate if you use this method! Use it for finally: style blocks. 这个方法添加同一个callback到两个处理链.在你使用这个方法的时候记住第一个参数是不确定的.把它看作是finally块来看待吧.
  • callback(result)
    • Run success callbacks with the given result. This can only be run once. Later calls to this or errback will raise twisted.internet.defer.AlreadyCalledError. If further callbacks or errbacks are added after this point, addCallbacks will run the callbacks immediately.

      使用给定的result调用成功执行时的callback.这个函数只能被调用一次.再次调用这个函数或者errback会导致一个twisted.internet.defer.AlreadyCalledError错误.如果更多的callback或者errback在这一点后被添加.addCallbacks会立即调用callback.

  • errback(failure)
    • Run error callbacks with the given failure. This can only be run once. Later calls to this or callback will raise twisted.internet.defer.AlreadyCalledError. If further callbacks or errbacks are added after this point, addCallbacks will run the callbacks immediately.

      使用给定的failure调用错误回调函数.这个函数只能被调用一次.再次调用这个函数或者callback会导致一个twisted.internet.defer.AlreadyCalledError错误.如果更多的callback或者errback在这一点后被添加,addCallbacks会立即调用callbacks.

5.2. 延迟处理链(Chaining Deferreds)

If you need one Deferred to wait on another, all you need to do is return a Deferred from a method added to addCallbacks. Specifically, if you return Deferred B from a method added to Deferred A using A.addCallbacks, Deferred A's processing chain will stop until Deferred B's .callback() method is called; at that point, the next callback in A will be passed the result of the last callback in Deferred B's processing chain at the time.

如果需要一个Deferred等待另一个,只需要从添加到addCallbacks的方法中返回一个Deferred.特别的,如果从一个添加到Deferred A(使用A.addCallbacks)的方法返回Deferred B, Deferred A的处理链会等待Deferred B的.callback()方法被调用;然后,A的下一个callback调用时就会传入B的处理链的最后一个callback返回的结果.

If this seems confusing, don't worry about it right now -- when you run into a situation where you need this behavior, you will probably recognize it immediately and realize why this happens. If you want to chain deferreds manually, there is also a convenience method to help you.

可能你现在会觉得有些乱,不过先不要着急 -- 当你遇到需要这种行为的情况的时候,你会马上想起这些并且明白到底发生了什么.如果你想手工来使处理链延迟,也有一个方便的方法可以调用

  • chainDeferred(otherDeferred)
    • Add otherDeferred to the end of this Deferred's processing chain. When self.callback is called, the result of my processing chain up to this point will be passed to otherDeferred.callback. Further additions to my callback chain do not affect otherDeferred 添加otherDeferred到这个Deferred的处理链的末尾.当self.callback被调用的时候,我的处理链在该点的最后结果会传递给otherDeferred.callback. 后面添加到我的回调链的方法不会对otherDeferred产生影响. This is the same as self.addCallbacks(otherDeferred.callback, otherDeferred.errback) 这和self.addCallbacks(otherDeferred.callback, otherDeferred.errback)是一样的.

5.3. 自动化错误情况(Automatic Error Conditions)

  • setTimeout(seconds[, timoutFunc])
    • Set a timeout function to be triggered if this Deferred is not called within that time period. By default, this will raise a TimeoutError after seconds.

      设置一个超时回调函数,如果在指定的一段时间内Deferred没有被调用,这个函数就会被触发.缺省情况会抛出一个TimeoutError.

5.4. 打断一下,马上回来:技术细节(A Brief Interlude: Technical Details)

Deferreds greatly simplify the process of writing asynchronous code by providing a standard for registering callbacks, but there are some subtle and sometimes confusing rules that you need to follow if you are going to use them. This mostly applies to people who are writing new systems that use Deferreds internally, and not writers of applications that just add callbacks to Deferreds produced and processed by other systems. Nevertheless, it is good to know.

Deferred通过提供标准的注册回调的方法极大的简化了编写同步代码的过程,但是有一些容易混淆的地方和细节一定要搞清楚.这个主要适用于那些编制信的系统而在内部使用Deferred的人们,而不是那些编写需要处理和其他系统交叉使用callback和Deferred应用程序的人.当然,他们也最好知道这些.

Deferreds are one-shot. A generalization of the Deferred API to generic event-sources is in progress -- watch this space for updates! -- but Deferred itself is only for events that occur once. You can only call Deferred.callback or Deferred.errback once. The processing chain continues each time you add new callbacks to an already-called-back-to Deferred.

Deferred只有一次.Deferred API概括来讲就是在执行过程中的一般事件源 -- 这里会在将来被更新! -- 但是,Deferred自己只用于只能被触发一次的事件.Deferred.callback或者Deferred.errback都只能被调用一次.在每次添加新的callback到已经调用过callback的Deferred的调用链的时候调用链都会继续执行.

The important consequence of this is that sometimes, addCallbacks will call its argument synchronously, and sometimes it will not. In situations where callbacks modify state, it is highly desirable for the chain of processing to halt until all callbacks are added. For this, it is possible to pause and unpause a Deferred's processing chain while you are adding lots of callbacks.

这里有一个重要的推论就是,addCallbacks会同步的调用它的参数,但并不总是这样.在callbacks修改状态的情况下,希望处理能够停止直到所有的callbacks都被添加.这就希望在添加大量callbacks的时候可以暂停,恢复处理链的执行.(Jerry:就是说添加回调会改变回调链本身,所以这个时候希望回调链的执行停下来).

Be careful when you use these methods! If you pause a Deferred, it is your responsibility to make sure that you unpause it; code that calls callback or errback should never call unpause, as this would negate its usefulness!

使用这些方法的时候一定要小心!如果你暂停了Deferred,你就有责任去恢复它;千万不要callback和errback中调用unpause,这样作没有用.

5.5. 高级用法之处理链控制(Advanced Processing Chain Control)

  • pause()
    • Cease calling any methods as they are added, and do not respond to callback, until self.unpause() is called. 暂停调用添加的任何方法,也不再响应callback,直到self.unpause()被调用.
  • unpause()
    • If callback has been called on this Deferred already, call all the callbacks that have been added to this Deferred since pause was called. 如果Deferred的callback已经被调用,就调用所有在pause()调用以后被添加到处理链的callbacks. Whether it was called or not, this will put this Deferred in a state where further calls to addCallbacks or callback will work as normal. 无论它是否已经被调用,这样做都会使Deferred回到普通的状态,在这个状态下addCallbacks或者callback都会正常工作.

6. 处理同步或异步结果(Handling either synchronous or asynchronous results)

In some applications, there are functions that might be either asynchronous or synchronous. For example, a user authentication function might be able to check in memory whether a user is authenticated, allowing the authentication function to return an immediate result, or it may need to wait on network data, in which case it should return a Deferred to be fired when that data arrives. However, a function that wants to check if a user is authenticated will then need to accept both immediate results and Deferreds.

在一些应用程序中,函数可能是同步执行的也可能是异步执行的.例如,一个用户验证函数可能只是在内存中检查用户是否是认证的然后立即返回结果,也可能需要等待网络数据,这时就需要返回Deferred,它可以在数据到达的时候被触发.而作为一个需要检查用户是否被认证的函数来说,它就应该可以接受直接结果也可以接受Deferred.

In this example, the library function authenticateUser uses the application function isValidUser to authenticate a user:

在这个例子中,库函数authenticateUser()调用了应用函数isValidUser来验证用户:

   1 def authenticateUser(isValidUser, user):
   2     if isValidUser(user):
   3         print "User is authenticated"
   4     else:
   5         print "User is not authenticated"

However, it assumes that isValidUser returns immediately, whereas isValidUser may actually authenticate the user asynchronously and return a Deferred. It is possible to adapt this trivial user authentication code to accept either a synchronous isValidUser or an asynchronous isValidUser, allowing the library to handle either type of function. It is, however, also possible to adapt synchronous functions to return Deferreds. This section describes both alternatives: handling functions that might be synchronous or asynchronous in the library function (authenticateUser) or in the application code.

不管怎样,它假设isValidUser()会立即返回结果.但是isValidUser()却有可能实际上是用异步的方式验证用户而返回Deferred.可以改写这个函数让它可以接受同步的IsValidUser()也可以接受异步的IsValidUser(),这样库函数就可以接受两种类型的验证调用.其实,还可以改写同步函数让它也返回Deferred.这节描述了这两种方法:改写库函数(authenticateUser)使得可以适应同步和异步两种方式,或者改写应用程序代码.

6.1. 在库代码中处理可能的延迟(Handling possible Deferreds in the library code)

Here is an example of a synchronous user authentication function that might be passed to authenticateUser:

下面是可以传给authenticateUser()的同步用户认证函数的例子:

   1 def synchronousIsValidUser(d, user):
   2     return user in ["Alice", "Angus", "Agnes"]

However, here's an asynchronousIsValidUser function that returns a Deferred:

下面是返回Deferred的asynchronousIsValidUser()函数:

   1 from twisted.internet import reactor
   2 
   3 def asynchronousIsValidUser(d, user):
   4     d = Deferred()
   5     reactor.callLater(2, d.callback, user in ["Alice", "Angus", "Agnes"])
   6     return d

Our original implementation of authenticateUser expected isValidUser to be synchronous, but now we need to change it to handle both synchronous and asynchronous implementations of isValidUser. For this, we use maybeDeferred to call isValidUser, ensuring that the result of isValidUser is a Deferred, even if isValidUser is a synchronous function:

原来authenticateUser()的实现期望isValidUser()以同步方式工作,但现在我们需要改变它以处理同步和异步两种方式的isValideUser()的实现.我们使用maybeDeferred来调用isValidUser(),保证了isValidUser()的结果一定是Deferred,就算它是同步方式工作也一样:

   1 from twisted.internet import defer
   2 
   3 def printResult(result):
   4     if result:
   5         print "User is authenticated"
   6     else:
   7         print "User is not authenticated"
   8 
   9 def authenticateUser(isValidUser, user):
  10     d = defer.maybeDeferred(isValidUser, user)
  11     d.addCallback(printResult)

Now isValidUser could be either synchronousIsValidUser or asynchronousIsValidUser.

现在isValidUser()可以是synchronousIsValidUser()也可以是asynchronousIsValidUser().

6.2. 在异步函数中返回Deferred(Returning a Deferred from synchronous functions)

An alternative is for authenticateUser to require that the implementation of isValidUser return a Deferred:

authenticateUser()的另一个选择是把isValidUser()实现为返回Deferred:

   1 def printResult(result):
   2     if result:
   3         print "User is authenticated"
   4     else:
   5         print "User is not authenticated"
   6 
   7 def authenticateUser(isValidUser, user):
   8     d = isValidUser(user)
   9     d.addCallback(printResult)

In this case, the author of synchronousIsValidUser would use defer.succeed to return a fired Deferred which will call the first callback with result, rather than returning the result itself:

在这个例子中,synchronousIsValidUser()的作者使用defer.succeed返回一个已经触发的Deferred,它会以result为参数调用第一个callback,而不是返回result自己.

   1 from twisted.internet import defer
   2 
   3 def immediateIsValidUser(d, user):
   4     result = user in ["Alice", "Angus", "Agnes"]
   5     return defer.succeed(result)

7. 延迟链表(DeferredList)

Sometimes you want to be notified after several different events have all happened, rather than waiting for each one individually. For example, you may want to wait for all the connections in a list to close. twisted.internet.defer.DeferredList is the way to do this.

有时你想要在几个不同的事件都发生后才收到通知,而不是每个事件都触发一次.例如:你可能想等待一个列表中所有的连接都关闭.twisted.internet.defer.DeferredList可以做这件事.

To create a DeferredList from multiple Deferreds, you simply pass a list of the Deferreds you want it to wait for:

从多个Deferred创建DeferredList,只需要简单的传递列表给想要等待的Deferred.

   1 # Creates a DeferredList
   2 dl = defer.DeferredList([deferred1, deferred2, deferred3])

You can now treat the DeferredList like an ordinary Deferred; you can call addCallbacks and so on. The DeferredList will call its callback when all the deferreds have completed. The callback will be called with a list of the results of the Deferreds it contains, like so:

你可以像对待普通的Deferred一样对待DeferredList;可以调用addCallbacks等等.DeferredList会在所有的deferred都完成的时候调用他的callback.callback会被传入一列包含Deferred的结果,就像下面这样:

   1 def printResult(result):
   2     print result
   3 deferred1 = defer.Deferred()
   4 deferred2 = defer.Deferred()
   5 deferred3 = defer.Deferred()
   6 dl = defer.DeferredList([deferred1, deferred2, deferred3])
   7 dl.addCallback(printResult)
   8 deferred1.callback('one')
   9 deferred2.errback('bang!')
  10 deferred3.callback('three')
  11 # At this point, dl will fire its callback, printing:
  12 #     [(1, 'one'), (0, 'bang!'), (1, 'three')]
  13 # (note that defer.SUCCESS == 1, and defer.FAILURE == 0)

A standard DeferredList will never call errback.

标准的DeferredList永远不会调用errback.

Note: 
If you want to apply callbacks to the individual Deferreds that go into the DeferredList, you should be careful about when those
callbacks are added. The act of adding a Deferred to a DeferredList inserts a callback into that Deferred (when that callback is
run, it checks to see if the DeferredList has been completed yet). The important thing to remember is that it is this callback
which records the value that goes into the result list handed to the DeferredList's callback.

注意:
如果你想为DeferredList中的每个Deferred单独的应用callback,就应该在添加那些callback的时候十分小心.添加一个Deferred到DeferredList会
插入一个callback到那个Deferred(当callback运行的时候,它会检查DeferredList是否已经都完成了).要记住的是:就是这个记录值的callback,会
被加入到处理DeferredList的callback的结果列表

Therefore, if you add a callback to the Deferred after adding the Deferred to the DeferredList, the value returned by that callback will not be given to the DeferredList's callback. To avoid confusion, we recommend not adding callbacks to a Deferred once it has been used in a DeferredList.

然而,如果添加一个callback给一个已经添加到DeferredList的Deferred,callback的返回值不会传递给DeferredList的callback.为了避免这种混乱,建议一旦把Deferred添加到DeferredList后就不要再给这个Deferred添加任何callback. }}}

   1 def printResult(result):
   2     print result
   3 def addTen(result):
   4     return result + " ten"
   5 
   6 # Deferred gets callback before DeferredList is created
   7 deferred1 = defer.Deferred()
   8 deferred2 = defer.Deferred()
   9 deferred1.addCallback(addTen)
  10 dl = defer.DeferredList([deferred1, deferred2])
  11 dl.addCallback(printResult)
  12 deferred1.callback("one") # fires addTen, checks DeferredList, stores "one ten"
  13 deferred2.callback("two")
  14 # At this point, dl will fire its callback, printing:
  15 #     [(1, 'one ten'), (1, 'two')]
  16 
  17 # Deferred gets callback after DeferredList is created
  18 deferred1 = defer.Deferred()
  19 deferred2 = defer.Deferred()
  20 dl = defer.DeferredList([deferred1, deferred2])
  21 deferred1.addCallback(addTen) # will run *after* DeferredList gets its value
  22 dl.addCallback(printResult)
  23 deferred1.callback("one") # checks DeferredList, stores "one", fires addTen
  24 deferred2.callback("two")
  25 # At this point, dl will fire its callback, printing:
  26 #     [(1, 'one), (1, 'two')]

7.1. 其它的行为(Other behaviours)

DeferredList accepts two keywords arguments that modify its behaviour: fireOnOneCallback, fireOnOneErrback and consumeErrors. If fireOnOneCallback is set, the DeferredList will immediately call its callback as soon as any of its Deferreds call their callback. Similarly, fireOnOneErrback will call errback as soon as any of the Deferreds call their errback. Note that DeferredList is still one-shot, like ordinary Deferreds, so after a callback or errback has been called the DeferredList will do nothing further (it will just silently ignore any other results from its Deferreds).

DeferredList接受两个关键字参数来更改它的行为: fireOnOneCallback, fireOnOneErrback和consumeErrors.如果fireOnOneCallback被设置,只要任何DeferredList中的一个Deferred调用callback,DeferredList就会立即调用它的callback.类似的,fireOnOneErrback会在任何一个Deferred调用errback的时候调用它的errback.注意DeferredList也是只会有一次,就像普通的Deferred,在一个callback或者errback被调用后DeferredList以后就不会再被调用(只会悄无声息的忽略任何Deferred的结果).

The fireOnOneErrback option is particularly useful when you want to wait for all the results if everything succeeds, but also want to know immediately if something fails.

当你想等待所有的结果都成功的可以使用选项fireOnOneErrback,这样也可以在任何一个结果失败的时候立即收到通知.

The consumeErrors argument will stop the DeferredList from propagating any errors along the callback chains of any Deferreds it contains (usually creating a DeferredList has no effect on the results passed along the callbacks and errbacks of their Deferreds). Stopping errors at the DeferredList with this option will prevent Unhandled error in Deferred warnings from the Deferreds it contains without needing to add extra errbacks[1].

参数consumeErrors会使DeferredList从它包含的任何一个Deferred的处理链中的错误传播中停止(通常情况下创建一个DeferredList不会对它包含的Deferred的callback和errback产生任何影响).使用这个选项停止DeferredList的错误处理会阻止在Deferred(该Deferred指已经加入到DeferredList中的Deferred)的未处理错误处理而不需要添加额外的errback[脚注1].

8. 脚注(Footnotes)

Unless of course a later callback starts a fresh error — but as we've already noted, adding callbacks to a Deferred after its used in a DeferredList is confusing and usually avoided.

当然,除非callback又产生新的错误 - 但是就像我们前面提到的,在将Deferred添加到DeferredList之后再给Deferred添加callback是应该被避免的.

  • 翻译 -- Jerry Marx.

(目录)Index

Version: 1.3.0