An Introduction to Asynchronous Programming with Twisted

The previous chapter derived Twisted’s event-driven architecture from first principles. Twisted programs, like all event-driven programs, make concurrency easier at the expense of making data flow control more difficult. An event-driven program does not a

  • PDF / 479,627 Bytes
  • 49 Pages / 504 x 720 pts Page_size
  • 91 Downloads / 241 Views

DOWNLOAD

REPORT


An Introduction to Asynchronous Programming with Twisted The previous chapter derived Twisted’s event-driven architecture from first principles. Twisted programs, like all event-driven programs, make concurrency easier at the expense of making data flow control more difficult. An event-driven program does not automatically have its execution suspended by blocking I/O when it sends more data than a receiving party can handle. It is the program’s responsibility to determine when this occurs and how to deal with it. The way data flows between communicating parties also affects the way that it flows within a single program. As a result, the strategies for composing different components of an event-driven applications differ from those used in blocking programs.

Event Handlers and Composition Consider a program that is not event-driven and uses blocking I/O to perform a network operation: def requestField(url, field):     results = requests.get(url).json()     return results[field]

© Mark Williams, Cory Benfield, Brian Warner, Moshe Zadka, Dustin Mitchell, Kevin Samuel, Pierre Tardy 2019 M. Williams et al., Expert Twisted, https://doi.org/10.1007/978-1-4842-3742-7_2

59

Chapter 2

An Introduction to Asynchronous Programming with Twisted

requestField retrieves a URL with the requests HTTP library, decodes the response’s body as JSON, and then returns the value of the requested field property from the resulting dictionary. requests uses blocking I/O, so a call to requestField pauses the entire program until the network operations required by the HTTP request complete. The function can thus assume that before it returns, results will be available for manipulation. Callers of this function can make the same assumption because requestField will block them until it has computed its result: def someOtherFunction(...):     ...     url = calculateURL(...)     value = requestField(url, 'someInteger')     return value + 1 x = someOtherFunction(...) Neither someOtherFunction nor the top-level x assignment can finish until requestField has retrieved the URL and extracted the value for the someInteger property from the JSON response. This is a kind composition: someOtherFunction invokes requestField to complete part of its own execution. We can make this clearer with explicit function composition: def someOtherFunction(value):     return value + 1 x = someOtherFunction(requestField(calculateURL(...), 'someInteger')) This code replaces someOtherFunction’s local variables with nested function calls, but is otherwise equivalent. Function composition is a fundamental tool for organizing programs. It allows a program to be factored, or split into separate units that form a whole whose behave exactly matches the non-factored version. This improves readability, reusability, and testability. Unfortunately, event handlers cannot be composed like someOtherFunction, requestField, and calculateURL. Consider a hypothetical, non-blocking version of requestField: def requestField(url, field):     ??? = nonblockingGet(url) 60

Chapter 2

An I