@@ -29,48 +29,38 @@ This document assumes familiarity with `uasyncio`. See [official docs](http://do
29
29
30
30
# 1. An alternative to callbacks in uasyncio code
31
31
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.
74
64
75
65
###### [ Contents] ( ./EVENTS.md#0-contents )
76
66
0 commit comments