2 minutes
Exécution de code bloquant dans un runtime async en Rust
Introduction
Dans cet article nous allons voir comment exécuter du code bloquant dans un runtime async en Rust.
Schéma visuel
Thread principal (async) Thread bloquant
| |
| spawn_blocking() -----------------------> |
| (retourne immédiatement) |
| | get_btc_stablecoin_pairs_sync()
| .await | (appel synchrone à Binance)
| (attend sans bloquer) |
| | (traitement...)
| <--------------------------------------- |
| (résultat reçu) |
| |
| .map_err() - gère JoinError |
| .map_err() - gère erreur métier |
| |
v v
Example de code
let btc_pairs = tokio::task::spawn_blocking(get_btc_stablecoin_pairs_sync)
.await
.map_err(|e| format!("Task join error: {}", e))?
.map_err(|e| format!("Error getting pairs: {}", e))?;
Equivalent avec annotations:
Décomposition étape par étape
pub async fn stream_btc_stablecoin_pairs() -> Result<(), Box<dyn std::error::Error>> {
log::info!("Fetching BTC/stablecoin pairs...");
// 1. Spawner un thread bloquant (retourne JoinHandle immédiatement)
let join_handle = tokio::task::spawn_blocking(get_btc_stablecoin_pairs_sync);
// 2. Attendre le résultat du thread (async, ne bloque pas le runtime)
let join_result: Result<Result<Vec<String>, String>, JoinError> = join_handle.await;
// 3. Gérer l'erreur de join (si le thread a paniqué)
let inner_result: Result<Vec<String>, String> = join_result
.map_err(|e| format!("Task join error: {}", e))?;
// 4. Gérer l'erreur métier (si l'API a échoué)
let btc_pairs: Vec<String> = inner_result
.map_err(|e| format!("Error getting pairs: {}", e))?;
// Reste du code...
log::info!("Found {} BTC/stablecoin pairs", btc_pairs.len());
// ...
}
Ce qu’il s’est passé étape par étape:
-
tokio::task::spawn_blocking(get_btc_stablecoin_pairs_sync)- Lance la fonction
get_btc_stablecoin_pairs_syncdans un thread dédié aux opérations bloquantes - Retourne immédiatement un
JoinHandle<Result<Vec<String>, String>> - Cette fonction ne bloque pas le runtime async
- Lance la fonction
-
.await- On attend (de manière async) que le thread bloquant termine
- Pendant ce temps, le runtime tokio peut exécuter d’autres tâches
- Retourne
Result<Result<Vec<String>, String>, JoinError> - Le premier
Resultvient du.await(erreur de join) - Le second
Resultvient de notre fonction (erreur métier)
-
Premier
.map_err(...)?- Gère l’erreur de
JoinError(si le thread a paniqué) - Transforme
Result<Result<Vec<String>, String>, JoinError>enResult<Vec<String>, String>
- Gère l’erreur de
-
Deuxième
.map_err(...)?- Gère l’erreur de notre fonction (si l’API Binance a échoué)
- Transforme le
Result<Vec<String>, String>enVec<String>ou propage l’erreur
Résumé
- spawn_blocking : “Lance ce code bloquant dans un thread dédié”
- .await : “Attends (sans bloquer) que ce thread termine”
- Les deux .map_err()? : “Gère les deux niveaux d’erreurs possibles”