diff options
Diffstat (limited to 'vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/DEFERRABLES')
| -rw-r--r-- | vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/DEFERRABLES | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/DEFERRABLES b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/DEFERRABLES new file mode 100644 index 0000000..6e8856c --- /dev/null +++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/DEFERRABLES @@ -0,0 +1,246 @@ +EventMachine (EM) adds two different formalisms for lightweight concurrency +to the Ruby programmer's toolbox: spawned processes and deferrables. This +note will show you how to use deferrables. For more information, see the +separate document LIGHTWEIGHT_CONCURRENCY. + +=== What are Deferrables? + +EventMachine's Deferrable borrows heavily from the "deferred" object in +Python's "Twisted" event-handling framework. Here's a minimal example that +illustrates Deferrable: + + require 'eventmachine' + + class MyClass + include EM::Deferrable + + def print_value x + puts "MyClass instance received #{x}" + end + end + + EM.run { + df = MyClass.new + df.callback {|x| + df.print_value(x) + EM.stop + } + + EM::Timer.new(2) { + df.set_deferred_status :succeeded, 100 + } + } + + +This program will spin for two seconds, print out the string "MyClass +instance received 100" and then exit. The Deferrable pattern relies on +an unusual metaphor that may be unfamiliar to you, unless you've used +Python's Twisted. You may need to read the following material through +more than once before you get the idea. + +EventMachine::Deferrable is simply a Ruby Module that you can include +in your own classes. (There also is a class named +EventMachine::DefaultDeferrable for when you want to create one without +including it in code of your own.) + +An object that includes EventMachine::Deferrable is like any other Ruby +object: it can be created whenever you want, returned from your functions, +or passed as an argument to other functions. + +The Deferrable pattern allows you to specify any number of Ruby code +blocks (callbacks or errbacks) that will be executed at some future time +when the status of the Deferrable object changes. + +How might that be useful? Well, imagine that you're implementing an HTTP +server, but you need to make a call to some other server in order to fulfill +a client request. + +When you receive a request from one of your clients, you can create and +return a Deferrable object. Some other section of your program can add a +callback to the Deferrable that will cause the client's request to be +fulfilled. Simultaneously, you initiate an event-driven or threaded client +request to some different server. And then your EM program will continue to +process other events and service other client requests. + +When your client request to the other server completes some time later, you +will call the #set_deferred_status method on the Deferrable object, passing +either a success or failure status, and an arbitrary number of parameters +(which might include the data you received from the other server). + +At that point, the status of the Deferrable object becomes known, and its +callback or errback methods are immediately executed. Callbacks and errbacks +are code blocks that are attached to Deferrable objects at any time through +the methods #callback and #errback. + +The deep beauty of this pattern is that it decouples the disposition of one +operation (such as a client request to an outboard server) from the +subsequent operations that depend on that disposition (which may include +responding to a different client or any other operation). + +The code which invokes the deferred operation (that will eventually result +in a success or failure status together with associated data) is completely +separate from the code which depends on that status and data. This achieves +one of the primary goals for which threading is typically used in +sophisticated applications, with none of the nondeterminacy or debugging +difficulties of threads. + +As soon as the deferred status of a Deferrable becomes known by way of a call +to #set_deferred_status, the Deferrable will IMMEDIATELY execute all of its +callbacks or errbacks in the order in which they were added to the Deferrable. + +Callbacks and errbacks can be added to a Deferrable object at any time, not +just when the object is created. They can even be added after the status of +the object has been determined! (In this case, they will be executed +immediately when they are added.) + +A call to Deferrable#set_deferred_status takes :succeeded or :failed as its +first argument. (This determines whether the object will call its callbacks +or its errbacks.) #set_deferred_status also takes zero or more additional +parameters, that will in turn be passed as parameters to the callbacks or +errbacks. + +In general, you can only call #set_deferred_status ONCE on a Deferrable +object. A call to #set_deferred_status will not return until all of the +associated callbacks or errbacks have been called. If you add callbacks or +errbacks AFTER making a call to #set_deferred_status, those additional +callbacks or errbacks will execute IMMEDIATELY. Any given callback or +errback will be executed AT MOST once. + +It's possible to call #set_deferred_status AGAIN, during the execution a +callback or errback. This makes it possible to change the parameters which +will be sent to the callbacks or errbacks farther down the chain, enabling +some extremely elegant use-cases. You can transform the data returned from +a deferred operation in arbitrary ways as needed by subsequent users, without +changing any of the code that generated the original data. + +A call to #set_deferred_status will not return until all of the associated +callbacks or errbacks have been called. If you add callbacks or errbacks +AFTER making a call to #set_deferred_status, those additional callbacks or +errbacks will execute IMMEDIATELY. + +Let's look at some more sample code. It turns out that many of the internal +protocol implementations in the EventMachine package rely on Deferrable. One +of these is EM::Protocols::HttpClient. + +To make an evented HTTP request, use the module function +EM::Protocols::HttpClient#request, which returns a Deferrable object. +Here's how: + + require 'eventmachine' + + EM.run { + df = EM::Protocols::HttpClient.request( :host=>"www.example.com", + :request=>"/index.html" ) + + df.callback {|response| + puts "Succeeded: #{response[:content]}" + EM.stop + } + + df.errback {|response| + puts "ERROR: #{response[:status]}" + EM.stop + } + } + +(See the documentation of EventMachine::Protocols::HttpClient for information +on the object returned by #request.) + +In this code, we make a call to HttpClient#request, which immediately returns +a Deferrable object. In the background, an HTTP client request is being made +to www.example.com, although your code will continue to run concurrently. + +At some future point, the HTTP client request will complete, and the code in +EM::Protocols::HttpClient will process either a valid HTTP response (including +returned content), or an error. + +At that point, EM::Protocols::HttpClient will call +EM::Deferrable#set_deferred_status on the Deferrable object that was returned +to your program, as the return value from EM::Protocols::HttpClient.request. +You don't have to do anything to make this happen. All you have to do is tell +the Deferrable what to do in case of either success, failure, or both. + +In our code sample, we set one callback and one errback. The former will be +called if the HTTP call succeeds, and the latter if it fails. (For +simplicity, we have both of them calling EM#stop to end the program, although +real programs would be very unlikely to do this.) + +Setting callbacks and errbacks is optional. They are handlers to defined +events in the lifecycle of the Deferrable event. It's not an error if you +fail to set either a callback, an errback, or both. But of course your +program will then fail to receive those notifications. + +If through some bug it turns out that #set_deferred_status is never called +on a Deferrable object, then that object's callbacks or errbacks will NEVER +be called. It's also possible to set a timeout on a Deferrable. If the +timeout elapses before any other call to #set_deferred_status, the Deferrable +object will behave as is you had called set_deferred_status(:failed) on it. + + +Now let's modify the example to illustrate some additional points: + + require 'eventmachine' + + EM.run { + df = EM::Protocols::HttpClient.request( :host=>"www.example.com", + :request=>"/index.html" ) + + df.callback {|response| + df.set_deferred_status :succeeded, response[:content] + } + + df.callback {|string| + puts "Succeeded: #{string}" + EM.stop + } + + df.errback {|response| + puts "ERROR: #{response[:status]}" + EM.stop + } + } + + +Just for the sake of illustration, we've now set two callbacks instead of +one. If the deferrable operation (the HTTP client-request) succeeds, then +both of the callbacks will be executed in order. + +But notice that we've also made our own call to #set_deferred_status in the +first callback. This isn't required, because the HttpClient implementation +already made a call to #set_deferred_status. (Otherwise, of course, the +callback would not be executing.) + +But we used #set_deferred_status in the first callback in order to change the +parameters that will be sent to subsequent callbacks in the chain. In this +way, you can construct powerful sequences of layered functionality. If you +want, you can even change the status of the Deferrable from :succeeded to +:failed, which would abort the chain of callback calls, and invoke the chain +of errbacks instead. + +Now of course it's somewhat trivial to define two callbacks in the same +method, even with the parameter-changing effect we just described. It would +be much more interesting to pass the Deferrable to some other function (for +example, a function defined in another module or a different gem), that would +in turn add callbacks and/or errbacks of its own. That would illustrate the +true power of the Deferrable pattern: to isolate the HTTP client-request +from other functions that use the data that it returns without caring where +those data came from. + +Remember that you can add a callback or an errback to a Deferrable at any +point in time, regardless of whether the status of the deferred operation is +known (more precisely, regardless of when #set_deferred_status is called on +the object). Even hours or days later. + +When you add a callback or errback to a Deferrable object on which +#set_deferred_status has not yet been called, the callback/errback is queued +up for future execution, inside the Deferrable object. When you add a +callback or errback to a Deferrable on which #set_deferred_status has +already been called, the callback/errback will be executed immediately. +Your code doesn't have to worry about the ordering, and there are no timing +issues, as there would be with a threaded approach. + +For more information on Deferrables and their typical usage patterns, look +in the EM unit tests. There are also quite a few sugarings (including +EM::Deferrable#future) that make typical Deferrable usages syntactically +easier to work with. + |
