Event-Driven Programming

Event-driven programming focuses on events. Eventually, the flow of program depends upon events. Until now, we were dealing with either sequential or parallel execution model but the model having the concept of event-driven programming is called asynchronous model. Event-driven programming depends upon an event loop that is always listening for the new incoming events. The working of event-driven programming is dependent upon events. Once an event loops, then events decide what to execute and in what order. Following flowchart will help you understand how this works −

Driven

Python Module – Asyncio

Asyncio module was added in Python 3.4 and it provides infrastructure for writing single-threaded concurrent code using co-routines. Following are the different concepts used by the Asyncio module −

The event loop

Event-loop is a functionality to handle all the events in a computational code. It acts round the way during the execution of whole program and keeps track of the incoming and execution of events. The Asyncio module allows a single event loop per process. Followings are some methods provided by Asyncio module to manage an event loop −

  • loop = get_event_loop() − This method will provide the event loop for the current context.

  • loop.call_later(time_delay,callback,argument) − This method arranges for the callback that is to be called after the given time_delay seconds.

  • loop.call_soon(callback,argument) − This method arranges for a callback that is to be called as soon as possible. The callback is called after call_soon() returns and when the control returns to the event loop.

  • loop.time() − This method is used to return the current time according to the event loop’s internal clock.

  • asyncio.set_event_loop() − This method will set the event loop for the current context to the loop.

  • asyncio.new_event_loop() − This method will create and return a new event loop object.

  • loop.run_forever() − This method will run until stop() method is called.

Example

The following example of event loop helps in printing hello world by using the get_event_loop() method. This example is taken from the Python official docs.

import asyncio

def hello_world(loop):
   print('Hello World')
   loop.stop()

loop = asyncio.get_event_loop()

loop.call_soon(hello_world, loop)

loop.run_forever()
loop.close()

Output

>Hello World

Futures

This is compatible with the concurrent.futures.Future class that represents a computation that has not been accomplished. There are following differences between asyncio.futures.Future and concurrent.futures.Future −

  • result() and exception() methods do not take a timeout argument and raise an exception when the future isn’t done yet.

  • Callbacks registered with add_done_callback() are always called via the event loop’s call_soon().

  • asyncio.futures.Future class is not compatible with the wait() and as_completed() functions in the concurrent.futures package.

Example

The following is an example that will help you understand how to use asyncio.futures.future class.

import asyncio

async def Myoperation(future):
   await asyncio.sleep(2)
   future.set_result('Future Completed')

loop = asyncio.get_event_loop()
future = asyncio.Future()
asyncio.ensure_future(Myoperation(future))
try:
   loop.run_until_complete(future)
   print(future.result())
finally:
   loop.close()

Output

>Future Completed

Coroutines

The concept of coroutines in Asyncio is similar to the concept of standard Thread object under threading module. This is the generalization of the subroutine concept. A coroutine can be suspended during the execution so that it waits for the external processing and returns from the point at which it had stopped when the external processing was done. The following two ways help us in implementing coroutines −

async def function()

This is a method for implementation of coroutines under Asyncio module. Following is a Python script for the same −

import asyncio

async def Myoperation():
   print("First Coroutine")

loop = asyncio.get_event_loop()
try:
   loop.run_until_complete(Myoperation())

finally:
   loop.close()

Output

>First Coroutine

@asyncio.coroutine decorator

Another method for implementation of coroutines is to utilize generators with the @asyncio.coroutine decorator. Following is a Python script for the same −

import asyncio

@asyncio.coroutine
def Myoperation():
   print("First Coroutine")

loop = asyncio.get_event_loop()
try:
   loop.run_until_complete(Myoperation())

finally:
   loop.close()

Output

>First Coroutine

Tasks

This subclass of Asyncio module is responsible for execution of coroutines within an event loop in parallel manner. Following Python script is an example of processing some tasks in parallel.

import asyncio
import time
async def Task_ex(n):
   time.sleep(1)
   print("Processing {}".format(n))
async def Generator_task():
   for i in range(10):
      asyncio.ensure_future(Task_ex(i))
   int("Tasks Completed")
   asyncio.sleep(2)

loop = asyncio.get_event_loop()
loop.run_until_complete(Generator_task())
loop.close()

Output

>Tasks Completed
Processing 0
Processing 1
Processing 2
Processing 3
Processing 4
Processing 5
Processing 6
Processing 7
Processing 8
Processing 9

Transports

Asyncio module provides transport classes for implementing various types of communication. These classes are not thread safe and always paired with a protocol instance after establishment of communication channel.

Following are distinct types of transports inherited from the BaseTransport −

  • ReadTransport − This is an interface for read-only transports.

  • WriteTransport − This is an interface for write-only transports.

  • DatagramTransport − This is an interface for sending the data.

  • BaseSubprocessTransport − Similar to BaseTransport class.

Followings are five distinct methods of BaseTransport class that are subsequently transient across the four transport types −

  • close() − It closes the transport.

  • is_closing() − This method will return true if the transport is closing or is already closed.transports.

  • get_extra_info(name, default = none) − This will give us some extra information about transport.

  • get_protocol() − This method will return the current protocol.

Protocols

Asyncio module provides base classes that you can subclass to implement your network protocols. Those classes are used in conjunction with transports; the protocol parses incoming data and asks for the writing of outgoing data, while the transport is responsible for the actual I/O and buffering. Following are three classes of Protocol −

  • Protocol − This is the base class for implementing streaming protocols for use with TCP and SSL transports.

  • DatagramProtocol − This is the base class for implementing datagram protocols for use with UDP transports..

  • SubprocessProtocol − This is the base class for implementing protocols communicating with child processes through a set of unidirectional pipes.