Skip to content

Commit 3e738da

Browse files
committed
EVENTS.md: Clarify introductory text.
1 parent 1df1218 commit 3e738da

File tree

1 file changed

+32
-42
lines changed

1 file changed

+32
-42
lines changed

v3/docs/EVENTS.md

Lines changed: 32 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -29,48 +29,38 @@ This document assumes familiarity with `uasyncio`. See [official docs](http://do
2929

3030
# 1. An alternative to callbacks in uasyncio code
3131

32-
A hardware device like a pushbutton or a software object like an MQTT client
33-
is designed to respond to an external asynchronous event. At the device driver
34-
level there are two common approaches to handling this (see note below):
35-
1. The driver provides a method which blocks until the event occurs (e.g.
36-
[machine.uart.read][1r].
37-
2. The user specifies a callback function which runs when the event occurs
38-
(e.g.[umqtt.simple][2r]).
39-
40-
The first approach is incompatible with asynchronous code because the blocking
41-
method stalls the scheduler while it is waiting. The second solves this because
42-
the callback consumes no resources until the event actually occurs. However it
43-
is not without problems. There is no standard way to specify callbacks, nor is
44-
there a standard way to pass arguments to them or to retrieve a result. Further
45-
a user might want to launch a task rather than run a synchronous callback. All
46-
these problems can be solved, but solutions are _ad hoc_ and will vary between
47-
drivers.
48-
49-
For example, `umqtt.simple` has a `set_callback` method with no way to pass
50-
args. Other drivers will require the callback (and perhaps args) to be passed
51-
as a constructor arg. Some drivers provide a method enabling the callback's
52-
result to be retrieved.
53-
54-
Further, if a requirement has logic such as to "send a message if event A
55-
is followed by either event B or event C" you are likely to find yourself at
56-
the gates of a place commonly known as "callback hell".
57-
58-
The one merit of designing callbacks into drivers is that it enables users to
59-
access `uasyncio` code with purely synchronous code. Typical examples are GUIs
60-
such as [micro-gui][1m]). These application frameworks use asynchronous code
61-
internally but may be accessed with conventional synchronous code.
62-
63-
For asynchronous programmers the API's of drivers, and their internal logic,
64-
can be simplified by abandoning callbacks in favour of `Event` beaviour. In
65-
essence the driver might expose an `Event` instance or be designed to emulate
66-
an `Event`. No capability is lost because the application can launch a callback
67-
or task when the `Event` is set. With the design approach outlined below, the
68-
need for callbacks is much reduced.
69-
70-
Note the `Stream` mechanism provides another approach which works well with
71-
devices such as sockets and UARTs. It is arguably less well suited to handling
72-
arbitrary events, partly because it relies on
73-
[polling](./EVENTS.md#100-appendix-1-polling) under the hood.
32+
Callbacks have two merits. They are familiar, and they enable an interface
33+
which allows an asynchronous application to be accessed by synchronous code.
34+
GUI frameworks such as [micro-gui][1m] form a classic example: the callback
35+
interface may be accessed by synchronous or asynchronous code.
36+
37+
For the programmer of asynchronous applications, callbacks are largely
38+
unnecessary and their use can lead to bugs.
39+
40+
The idiomatic way to write an asynchronous function that responds to external
41+
events is one where the function pauses while waiting on the event:
42+
```python
43+
async def handle_messages(input_stream):
44+
while True:
45+
msg = await input_stream.readline()
46+
await handle_data(msg)
47+
```
48+
Callbacks are not a natural fit in this model. Viewing the declaration of a
49+
synchronous function, it is not evident how the function gets called or in what
50+
context the code runs. Is it an ISR? Is it called from another thread or core?
51+
Or is it a callback running in a `uasyncio` context? You cannot tell without
52+
trawling the code. By contrast, a routine such as the above example is a self
53+
contained process whose context and intended behaviour are evident.
54+
55+
The following steps can facilitate the use of asynchronous functions:
56+
1. Design device drivers to expose one or more bound `Event` objects.
57+
Alternatively design the driver interface to be that of an `Event`.
58+
2. Design program logic to operate on objects with an `Event` interface.
59+
60+
The first simplifies the design of drivers and standardises their interface.
61+
Users only need to know the names of the bound `Event` instances. By contast
62+
there is no standard way to specify callbacks, to define the passing of
63+
callback arguments or to define how to retrieve their return values.
7464

7565
###### [Contents](./EVENTS.md#0-contents)
7666

0 commit comments

Comments
 (0)