# Asyncio & Event loop

## Awaitable

* There are 3 types awaitable objects

1. Coroutines
2. Tasks - [asyncio.Task](https://docs.python.org/3/library/asyncio-task.html#asyncio.Task)
3. Futures - [asyncio.Future](https://docs.python.org/3/library/asyncio-future.html#asyncio.Future)

## Coroutine

* A coroutine is a specialized function in Python used for asynchronous programming.&#x20;
* It allows you to pause and resume its execution at certain points, making it ideal for handling tasks that involve waiting (e.g., for I/O operations, timers, or other asynchronous events).&#x20;
* Coroutines work in conjunction with an event loop, which schedules and manages their execution.

```python
async def my_coroutine():
    print("Start")
    await asyncio.sleep(1)  # Pause execution here
    print("Resume after 1 second")
```

#### **How Coroutines Work**

1. **Pausing Execution**: When a coroutine encounters an `await` expression, it pauses its execution and hands control back to the event loop.
2. **Resuming Execution**: The coroutine is resumed when the awaited task (e.g., `asyncio.sleep(1)`) is completed, allowing other tasks to run in the meantime.

## Task

* You can create separate tasks that run independently, allowing your program to continue without waiting for one task to finish.
* Here, `task1()` and `task2()` run concurrently, and the program doesn't block while waiting for one task to complete.

```python
import asyncio

async def task1():
    print("Task 1 started")
    await asyncio.sleep(2)  # Pause Task 1
    print("Task 1 completed")

async def task2():
    print("Task 2 started")
    await asyncio.sleep(1)  # Pause Task 2
    print("Task 2 completed")

async def main():
    # Create both tasks
    t1 = asyncio.create_task(task1())
    t2 = asyncio.create_task(task2())
    
    # Wait for both to complete
    await t1
    await t2

asyncio.run(main())
/** 
    Task 1 started
    Task 2 started
    Task 2 completed
    Task 1 completed
**/
```

* `asyncio.gather` lets you wait for multiple tasks at the same time without blocking others. It collects results from all the coroutines when they're done.

```python
import asyncio

async def task1():
    await asyncio.sleep(5)
    print("Task 1 done")

async def task2():
    print("Task 2 running")
    await asyncio.sleep(2)
    print("Task 2 done")

async def main():
    results = await asyncio.gather(task1(), task2())
    print("All tasks completed:", results)

asyncio.run(main())
```

## Event Loop

* Event loops use cooperative scheduling: an event loop runs one Task at a time. While a Task awaits for the completion of a Future, the event loop runs other Tasks, callbacks, or performs IO operations
* When you call `asyncio.run(main())`, it creates a new event loop, runs the `main()` coroutine inside that loop, and waits for the coroutine to finish. It ensures the `main()` coroutine executes to completion.
* **How the Event Loop Handles Tasks**
  1. **Concurrency**: The event loop enables tasks to run concurrently, meaning it can switch between tasks when one is waiting (e.g., during `await asyncio.sleep()` or an I/O operation). However, tasks do not actually run in parallel unless you explicitly use threads or processes.
  2. **Single-threaded Nature**: The event loop operates in a single thread. It executes one task at a time but quickly switches between them based on when they are ready to progress (non-blocking behavior).
  3. **Task Switching**:
     * When a task reaches an `await` point (e.g., waiting for a network response or a timer), it yields control back to the event loop.
     * The event loop then checks its queue of pending tasks and runs the next task that is ready to continue.
     * This gives the appearance of "simultaneous" execution but is actually task scheduling.

## Vs Nodejs

### **Similarities**

1. **Asynchronous Programming**: Both Python's `asyncio` and Node.js's event loop enable non-blocking, asynchronous operations. This makes them ideal for I/O-bound tasks like handling multiple network requests.
2. **Single-threaded**: Both event loops run on a single thread, processing tasks in an order determined by the scheduler.
3. **Callbacks and Promises**: Node.js  primarily uses callbacks and promises for asynchronous handling, while Python uses `async/await`, which is conceptually similar to promises.

### **Differences**

1. **Native Integration**:
   * Node.js  has its event loop implemented directly in its runtime, based on **libuv**, a C library. The event loop is deeply integrated with Node's non-blocking I/O functions.
   * Python's `asyncio` is a library/module implemented on top of Python's runtime, rather than being a part of the core runtime.
2. **Task Scheduling**:
   * In Node.js, the event loop has phases (e.g., timers, I/O callbacks, idle/prepare, poll, check, and close callbacks). It cycles through these phases repeatedly in the order defined by `libuv`.
   * In Python's `asyncio`, tasks are managed within an `asyncio` event loop using coroutines. Python doesn't have phases like Node.js  but schedules tasks and runs them when their I/O operations complete.
3. **Concurrency**:
   * Node.js  is inherently single-threaded, but it uses its thread pool (via `libuv`) to offload heavy operations like file system tasks or cryptographic operations.
   * Python's `asyncio` can use `await` and coroutines for asynchronous tasks, and for true concurrency, Python can leverage threading or multiprocessing in addition to the `asyncio` event loop.
4. **API Design**:
   * Node.js  APIs heavily use callbacks and promise-based APIs, making it more callback-centric.
   * Python's `asyncio` uses coroutines with `async def` and `await`, which are often considered more readable.

## References

{% embed url="<https://myapollo.com.tw/blog/begin-to-asyncio/>" %}

{% embed url="<https://bbc.github.io/cloudfit-public-docs/asyncio/asyncio-part-2>" %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://petercheng7788.gitbook.io/developer-note/programming-language/python/asyncio-and-event-loop.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
