peterhinch / micropython-mqtt

A 'resilient' asynchronous MQTT driver. Recovers from WiFi and broker outages.
MIT License
549 stars 116 forks source link

where is heartbeat() awaited #108

Closed investigatronica closed 1 year ago

investigatronica commented 1 year ago

I'm relatively new with asyncio and I'm trying to understand your code.

I can't figure out when/where asyncio.create_task(heartbeat()) is awaited or which loop it belongs to?

https://github.com/peterhinch/micropython-mqtt/blob/94b97f57c7bc4d56fe5edb3106f6ea06c84080ac/mqtt_as/clean.py#L64

beetlegigg commented 1 year ago

I offer you this explanation as a fellow pupil of @peterhinch async tutorials which are worth a good read.

The asyncio.create_task(heartbeat()) creates a task an puts it on the async scheduler ready to action when the scheduler gets a look in. The scheduler gets a look in when a co-routine function is awaited, or an await asyncio.sleep() is called. Then, in a round robin fashion, the tasks in the scheduler are run. The co-routine that is awaited to kick the scheduler off can be any task, (or a sleep) and its not necessary to specifically await the heartbeat task. The following code snippet will illustrate this

import uasyncio as asyncio
async def heartbeat():
    print('heartbeat')
async def justprint():
    print('justprint')
async def main():
    await justprint()
asyncio.create_task(heartbeat())
asyncio.run(main())

The resulting printout is heartbeat justprint

As you see creating the heatbeat task is the first thing that is created and put in the scheduler. The only function awaited is justprint() that is called when the asyncio.run(main()) is called. That was enough for the async schedule to run the heartbeat task, and it was executed first as it was the first item in the scheduler's list. (Peter will downgrade me to dunce if my answer is not correct)

peterhinch commented 1 year ago

A task instantiated by create_task() will run until it's terminated or is cancelled. There is a single event loop, but normally you can regard it as a uasyncio internal object. Most modern uasyncio code does not need to reference it.