⚡ Async/Await: Lập Trình Bất Đồng Bộ
🎯 Mục Tiêu Bài Học
Sau khi hoàn thành bài học này, bạn sẽ:
- ✅ Hiểu async vs sync programming
- ✅ Sử dụng
async fnvà.await - ✅ Hiểu Futures trong Rust
- ✅ So sánh async vs threads
- ✅ Blocking vs non-blocking
- ✅ Giới thiệu async runtimes
🤔 Async Programming Là Gì?
Ẩn Dụ Cuộc Sống: Nhà Hàng Phục Vụ
Async giống như cách nhà hàng phục vụ khách:
🍽️ Sync (Đồng Bộ):
- Phục vụ từng bàn một
- Đợi món xong mới sang bàn khác
- Chậm, lãng phí thời gian chờ
⚡ Async (Bất Đồng Bộ):
- Nhận order nhiều bàn
- Khi bếp nấu, phục vụ bàn khác
- Quay lại khi món sẵn sàng
- Hiệu quả hơn!
🦀 Async Trong Rust:
- Không block khi chờ I/O
- Xử lý nhiều tasks đồng thời
- Lightweight - không cần nhiều threads
- Zero-cost abstractions
Ví Dụ Cơ Bản
// Cần thêm tokio vào Cargo.toml:
// tokio = { version = "1", features = ["full"] }
use tokio::time::{sleep, Duration};
async fn say_hello() {
println!("Hello");
sleep(Duration::from_secs(1)).await;
println!("World");
}
#[tokio::main]
async fn main() {
say_hello().await;
}
📝 Async Functions
Định Nghĩa Async Function
// Sync function
fn sync_function() -> String {
String::from("Hello")
}
// Async function
async fn async_function() -> String {
String::from("Hello")
}
// async fn returns Future<Output = String>
Calling Async Functions
async fn fetch_data() -> i32 {
42
}
#[tokio::main]
async fn main() {
// Phải dùng .await để get kết quả
let data = fetch_data().await;
println!("Data: {}", data);
}
Multiple Async Calls
use tokio::time::{sleep, Duration};
async fn task1() -> String {
sleep(Duration::from_secs(1)).await;
String::from("Task 1 done")
}
async fn task2() -> String {
sleep(Duration::from_secs(1)).await;
String::from("Task 2 done")
}
#[tokio::main]
async fn main() {
// Sequential - takes 2 seconds
let r1 = task1().await;
let r2 = task2().await;
println!("{}", r1);
println!("{}", r2);
}
⏳ .await Keyword
Await là gì?
use tokio::time::{sleep, Duration};
async fn slow_operation() -> i32 {
println!("Starting...");
sleep(Duration::from_secs(2)).await; // Pause here
println!("Done!");
42
}
#[tokio::main]
async fn main() {
let result = slow_operation().await;
println!("Result: {}", result);
}
Await Multiple Operations
use tokio::time::{sleep, Duration};
async fn operation1() -> i32 {
sleep(Duration::from_secs(1)).await;
10
}
async fn operation2() -> i32 {
sleep(Duration::from_secs(1)).await;
20
}
async fn operation3() -> i32 {
sleep(Duration::from_secs(1)).await;
30
}
#[tokio::main]
async fn main() {
let a = operation1().await;
let b = operation2().await;
let c = operation3().await;
println!("Sum: {}", a + b + c); // Takes 3 seconds total
}
🔀 Concurrent Execution
tokio::join! - Run Concurrently
use tokio::time::{sleep, Duration};
async fn task1() -> String {
sleep(Duration::from_secs(2)).await;
String::from("Task 1")
}
async fn task2() -> String {
sleep(Duration::from_secs(2)).await;
String::from("Task 2")
}
#[tokio::main]
async fn main() {
// Run concurrently - only 2 seconds total!
let (r1, r2) = tokio::join!(task1(), task2());
println!("{}, {}", r1, r2);
}
tokio::spawn - Spawn Task
use tokio::time::{sleep, Duration};
#[tokio::main]
async fn main() {
let handle = tokio::spawn(async {
sleep(Duration::from_secs(1)).await;
println!("Task completed!");
42
});
println!("Task spawned");
let result = handle.await.unwrap();
println!("Result: {}", result);
}
Multiple Spawned Tasks
use tokio::time::{sleep, Duration};
#[tokio::main]
async fn main() {
let mut handles = vec![];
for i in 0..5 {
let handle = tokio::spawn(async move {
sleep(Duration::from_millis(100 * i)).await;
println!("Task {} done", i);
i
});
handles.push(handle);
}
for handle in handles {
let result = handle.await.unwrap();
println!("Result: {}", result);
}
}
🎯 Ví Dụ Thực Tế
Ví Dụ 1: Simulated API Calls
use tokio::time::{sleep, Duration};
async fn fetch_user(id: u32) -> String {
sleep(Duration::from_secs(1)).await;
format!("User {}", id)
}
async fn fetch_posts(user: &str) -> Vec<String> {
sleep(Duration::from_secs(1)).await;
vec![
format!("{}'s post 1", user),
format!("{}'s post 2", user),
]
}
#[tokio::main]
async fn main() {
let user = fetch_user(1).await;
println!("Got user: {}", user);
let posts = fetch_posts(&user).await;
for post in posts {
println!("- {}", post);
}
}
Ví Dụ 2: Concurrent API Calls
use tokio::time::{sleep, Duration};
async fn fetch_user(id: u32) -> String {
sleep(Duration::from_secs(1)).await;
format!("User {}", id)
}
#[tokio::main]
async fn main() {
let start = std::time::Instant::now();
// Fetch 3 users concurrently
let (u1, u2, u3) = tokio::join!(
fetch_user(1),
fetch_user(2),
fetch_user(3)
);
println!("Users: {}, {}, {}", u1, u2, u3);
println!("Time: {:?}", start.elapsed()); // ~1 second instead of 3!
}
Ví Dụ 3: Timeout Pattern
use tokio::time::{sleep, timeout, Duration};
async fn slow_operation() -> String {
sleep(Duration::from_secs(5)).await;
String::from("Done")
}
#[tokio::main]
async fn main() {
match timeout(Duration::from_secs(2), slow_operation()).await {
Ok(result) => println!("Result: {}", result),
Err(_) => println!("Operation timed out!"),
}
}