⏳ Async Operations
Học cách xử lý bất đồng bộ trong ứng dụng Flet. ⏱️ 15 phút
🎯 Mục tiêu
Phần tiêu đề “🎯 Mục tiêu”Nắm vững:
- Async event handlers
- Long-running tasks
- Progress indicators
- Background tasks
⚡ Tại sao cần Async?
Phần tiêu đề “⚡ Tại sao cần Async?”Khi thực hiện các tác vụ mất thời gian (API calls, file I/O, tính toán nặng), bạn cần async để:
- Không block UI
- Hiển thị loading indicators
- Cho phép cancel operations
🔄 Async với Declarative UI
Phần tiêu đề “🔄 Async với Declarative UI”Kết hợp @ft.observable với async handlers:
import flet as ftimport asyncio
@ft.observableclass LoadingState: is_loading: bool = False status: str = "Ready"
def start_loading(self): self.is_loading = True self.status = "Loading..."
def finish_loading(self, message: str = "Done! ✅"): self.is_loading = False self.status = message
state = LoadingState()
@ft.componentdef AsyncDemo(): async def fetch_data(_): state.start_loading() await asyncio.sleep(2) # Simulate API call state.finish_loading("Data loaded! ✅")
return ft.Column([ ft.ElevatedButton("Fetch Data", on_click=fetch_data), ft.ProgressRing(visible=state.is_loading), ft.Text(state.status), ], spacing=15)
async def main(page: ft.Page): page.title = "Async Demo" page.padding = 30 page.add(AsyncDemo())
ft.run(main)📊 Progress Indicator
Phần tiêu đề “📊 Progress Indicator”import flet as ftimport asyncio
@ft.observableclass ProgressState: progress: float = 0 status: str = "0%" is_running: bool = False
def update(self, value: float): self.progress = value self.status = f"{int(value * 100)}%"
def complete(self): self.is_running = False self.status = "Done! ✅"
def start(self): self.is_running = True self.progress = 0
state = ProgressState()
@ft.componentdef ProgressDemo(): async def start_process(_): state.start() for i in range(101): state.update(i / 100) await asyncio.sleep(0.03) state.complete()
return ft.Column([ ft.ProgressBar(value=state.progress, width=300), ft.Text(state.status, size=18), ft.ElevatedButton( "Start", on_click=start_process, disabled=state.is_running, ), ], spacing=15, horizontal_alignment=ft.CrossAxisAlignment.CENTER)
async def main(page: ft.Page): page.title = "Progress Demo" page.vertical_alignment = ft.MainAxisAlignment.CENTER page.horizontal_alignment = ft.CrossAxisAlignment.CENTER page.add(ProgressDemo())
ft.run(main)🌐 HTTP Requests với httpx
Phần tiêu đề “🌐 HTTP Requests với httpx”import flet as ftimport httpximport asyncio
@ft.observableclass ApiState: is_loading: bool = False result: str = "" error: str = ""
def start_fetch(self): self.is_loading = True self.result = "" self.error = ""
def set_result(self, data: str): self.is_loading = False self.result = data
def set_error(self, error: str): self.is_loading = False self.error = error
state = ApiState()
@ft.componentdef ApiDemo(): async def fetch_user(_): state.start_fetch() try: async with httpx.AsyncClient() as client: response = await client.get( "https://jsonplaceholder.typicode.com/users/1" ) data = response.json() state.set_result(f"Name: {data['name']}\nEmail: {data['email']}") except Exception as e: state.set_error(f"Error: {e}")
return ft.Column([ ft.ElevatedButton("Fetch User", on_click=fetch_user), ft.ProgressRing(visible=state.is_loading), ft.Text(state.result, selectable=True) if state.result else None, ft.Text(state.error, color=ft.Colors.RED) if state.error else None, ], spacing=15)
async def main(page: ft.Page): page.title = "API Demo" page.padding = 30 page.add(ApiDemo())
ft.run(main)⏸️ Cancelable Tasks
Phần tiêu đề “⏸️ Cancelable Tasks”import flet as ftimport asyncio
@ft.observableclass TaskState: status: str = "Idle" is_running: bool = False task = None
def set_status(self, status: str): self.status = status
def start(self): self.is_running = True
def stop(self): self.is_running = False
state = TaskState()
@ft.componentdef CancelableTaskDemo(): async def long_task(): state.start() try: for i in range(10): state.set_status(f"Processing... {i+1}/10") await asyncio.sleep(1) state.set_status("Completed! ✅") except asyncio.CancelledError: state.set_status("Cancelled ❌") finally: state.stop()
async def start_task(_): state.task = asyncio.create_task(long_task())
async def cancel_task(_): if state.task: state.task.cancel()
return ft.Column([ ft.Row([ ft.ElevatedButton("Start", on_click=start_task, disabled=state.is_running), ft.ElevatedButton("Cancel", on_click=cancel_task, disabled=not state.is_running), ]), ft.ProgressRing(visible=state.is_running), ft.Text(state.status, size=18), ], spacing=15)
async def main(page: ft.Page): page.title = "Cancelable Task" page.padding = 30 page.add(CancelableTaskDemo())
ft.run(main)🔄 Multiple Concurrent Tasks
Phần tiêu đề “🔄 Multiple Concurrent Tasks”import flet as ftimport asyncio
@ft.observableclass MultiTaskState: results: list = [] is_loading: bool = False
def clear(self): self.results = [] self.is_loading = True
def add_result(self, result: str): self.results = self.results + [result]
def complete(self): self.is_loading = False
state = MultiTaskState()
@ft.componentdef ResultsList(): if not state.results: return ft.Text("No results yet") return ft.Column([ ft.Text(r, color=ft.Colors.GREEN) for r in state.results ])
@ft.componentdef MultiTaskDemo(): async def fetch_one(name: str, delay: float): await asyncio.sleep(delay) state.add_result(f"✅ {name} loaded")
async def fetch_all(_): state.clear() await asyncio.gather( fetch_one("Users", 1.0), fetch_one("Products", 1.5), fetch_one("Orders", 0.8), ) state.add_result("🎉 All done!") state.complete()
return ft.Column([ ft.ElevatedButton("Fetch All", on_click=fetch_all, disabled=state.is_loading), ft.ProgressRing(visible=state.is_loading), ResultsList(), ], spacing=15)
async def main(page: ft.Page): page.title = "Multi Task Demo" page.padding = 30 page.add(MultiTaskDemo())
ft.run(main)⏰ Periodic Updates (Clock)
Phần tiêu đề “⏰ Periodic Updates (Clock)”import flet as ftimport asynciofrom datetime import datetime
@ft.observableclass ClockState: time: str = "" is_running: bool = True
def update_time(self): self.time = datetime.now().strftime("%H:%M:%S")
state = ClockState()
@ft.componentdef Clock(): return ft.Column([ ft.Text("Current Time:", size=20), ft.Text(state.time, size=48, weight=ft.FontWeight.BOLD), ], horizontal_alignment=ft.CrossAxisAlignment.CENTER)
async def main(page: ft.Page): page.title = "Clock" page.vertical_alignment = ft.MainAxisAlignment.CENTER page.horizontal_alignment = ft.CrossAxisAlignment.CENTER
page.add(Clock())
# Background task for clock updates async def update_clock(): while state.is_running: state.update_time() await asyncio.sleep(1)
asyncio.create_task(update_clock())
ft.run(main)💡 Best Practices
Phần tiêu đề “💡 Best Practices”[!TIP]
- Luôn hiển thị loading - Cho user biết đang xử lý
- Handle errors - Wrap trong try/except
- Cho phép cancel - Với long tasks
- Tránh block UI - Dùng async cho I/O operations
- Throttle updates - Không update quá nhanh
📋 Async Patterns
Phần tiêu đề “📋 Async Patterns”| Pattern | Use Case |
|---|---|
async def handler |
Event handlers với I/O |
asyncio.sleep() |
Delays, animations |
asyncio.gather() |
Multiple concurrent tasks |
asyncio.create_task() |
Background tasks |
task.cancel() |
Cancelable operations |
⏭️ Tiếp theo
Phần tiêu đề “⏭️ Tiếp theo”Tuyệt! Tiếp tục với bài cuối: Build & Deploy để đóng gói và phân phối ứng dụng.