Course de condition Async Await Null
Problème
Quand une opération asynchrone est en attente, du code externe peut définir la référence d'objet à null.
Quand l'await se termine, accéder à l'objet avec ! lève "Null check operator
used on a null value".
Contexte / Conditions déclenchantes
- Erreur :
Null check operator used on a null value - Modèle :
await someObject!.asyncMethod()suivi desomeObject!.property - L'objet peut être annulé par des opérations d'annulation/disposition/nettoyage
- Opérations concurrentes sur un état mutable partagé
- Généralement visible avec les objets de session, les gestionnaires de connexion, ou les références de service
Solution
Incorrect - L'objet peut devenir null pendant l'await :
Future<Result> waitForResponse() async {
// _session pourrait être défini à null pendant qu'on attend
final result = await _session!.waitForConnection();
if (result == null) {
// BUG: _session pourrait être null ici !
final state = _session!.state; // Lève une erreur de null check
...
}
}
void cancel() {
_session?.cancel();
_session = null; // Cela s'exécute pendant que waitForResponse attend
}
Correct - Capturer la référence locale avant l'await :
Future<Result> waitForResponse() async {
// Capturer la référence AVANT l'await
final session = _session!;
final result = await session.waitForConnection();
// Vérifier si annulé pendant l'await
if (_session == null) {
return Result.cancelled();
}
if (result == null) {
// Sécurisé - utilise la référence locale capturée
final state = session.state;
...
}
}
Vérification
- Déclencher l'opération d'annulation/disposition pendant que l'opération asynchrone est en cours
- Aucune erreur de null check ne devrait se produire
- L'opération devrait gérer l'annulation de manière gracieuse
Exemple - Modèle complet
class ConnectionManager {
Session? _session;
Future<ConnectionResult> connect() async {
if (_session == null) {
return ConnectionResult.failure('No session');
}
// Capturer avant l'await
final session = _session!;
final response = await session.waitForResponse(
timeout: Duration(minutes: 2),
);
// Vérifier si annulé pendant l'await
if (_session == null) {
return ConnectionResult.failure('Cancelled');
}
// Sécurisé d'utiliser la référence capturée
if (response == null) {
return ConnectionResult.failure(session.errorMessage);
}
return ConnectionResult.success(response);
}
void cancel() {
_session?.cancel();
_session?.dispose();
_session = null;
}
}
Notes
- Ce modèle s'applique à toute référence mutable qui peut être annulée de l'extérieur
- Courant dans Flutter/Dart avec les modèles de disposition, les providers Riverpod, et les sessions WebSocket
- La référence locale capturée maintient l'objet vivant pendant l'await
- Toujours ajouter une vérification null après l'await pour détecter l'annulation
- Considérer l'utilisation de modèles d'annulation
Completerpour des scénarios plus complexes
Modèles connexes
riverpod-ref-read-in-dispose- Problème similaire avec le cycle de vie de la ref Riverpodflutter-dispose-timer-test-failure- Callbacks de timer après disposition