-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexamples.py
More file actions
98 lines (76 loc) · 4.36 KB
/
Copy pathexamples.py
File metadata and controls
98 lines (76 loc) · 4.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
"""
Chapter 21: Asynchronous Programming
====================================
Original implementations exploring native async/await syntax,
the asyncio event loop, and concurrent task coordination.
Key concepts covered:
- Coroutines (async def / await)
- asyncio.gather() for ordered concurrent execution
- asyncio.as_completed() for out-of-order responsiveness
"""
import sys
import asyncio
import random
import time
sys.stdout.reconfigure(encoding="utf-8")
def section(title: str) -> None:
print(f"\n{'=' * 60}\n=== {title}\n{'=' * 60}")
# ─────────────────────────────────────────────────────────────────────────────
# 1. Native Coroutines
# ─────────────────────────────────────────────────────────────────────────────
async def fetch_data(id: int) -> str:
"""A native coroutine. Must be called with `await`."""
print(f" [Task {id}] Starting fetch...")
# await yields control back to the event loop, allowing other tasks to run
await asyncio.sleep(0.5)
print(f" [Task {id}] Finished!")
return f"Data_{id}"
async def run_part_1() -> None:
section("Part 1: Basic await")
# Sequential awaiting (Slow! Takes 1.0s total)
res1 = await fetch_data(1)
res2 = await fetch_data(2)
print(f" Results: {res1}, {res2}")
# ─────────────────────────────────────────────────────────────────────────────
# 2. asyncio.gather() (Concurrent & Ordered)
# ─────────────────────────────────────────────────────────────────────────────
async def run_part_2() -> None:
section("Part 2: asyncio.gather()")
# gather() schedules all coroutines to run CONCURRENTLY.
# It waits for all of them to finish, and returns the results
# in the exact same order they were passed in.
start_t = time.perf_counter()
# Runs in 0.5s total!
results = await asyncio.gather(
fetch_data(10),
fetch_data(11),
fetch_data(12)
)
elapsed = time.perf_counter() - start_t
print(f" Results: {results} (Took {elapsed:.2f}s)")
# ─────────────────────────────────────────────────────────────────────────────
# 3. asyncio.as_completed() (Out-of-Order UI)
# ─────────────────────────────────────────────────────────────────────────────
async def variable_fetch(id: int) -> str:
await asyncio.sleep(random.uniform(0.1, 0.9))
return f"Data_{id}"
async def run_part_3() -> None:
section("Part 3: asyncio.as_completed()")
# Create the coroutine objects (they don't start running yet!)
coros = [variable_fetch(i) for i in range(5)]
# as_completed yields futures exactly as they finish
for coro in asyncio.as_completed(coros):
# We must await the yielded future to get the actual result
result = await coro
print(f" Received: {result}")
# ─────────────────────────────────────────────────────────────────────────────
# MAIN EVENT LOOP
# ─────────────────────────────────────────────────────────────────────────────
async def main() -> None:
await run_part_1()
await run_part_2()
await run_part_3()
if __name__ == "__main__":
# asyncio.run() bootstraps the event loop, runs the main coroutine,
# and safely tears down the loop when finished.
asyncio.run(main())