În dezvoltarea asincronă, o problemă majoră este gestionarea erorilor care „scapă” din blocurile convenționale try/catch: dacă o excepție este aruncată într-un callback de timer, într-o microtask nesupravegheată sau într-un fir asincron desprins, ea poate provoca oprirea forțată a aplicației.
Zonele (Zones) din Dart reprezintă un mecanism extrem de puternic de încapsulare și contextualizare a execuției asincrone. O zonă funcționează ca un mediu de tip „sandbox” care poate intercepta toate erorile asincrone, poate injecta valori de context globale (asemănătoare cu ThreadLocal, dar asincrone) și poate suprascrie comportamentul funcțiilor standard de sistem precum print() sau Timer.
import 'dart:async';
void main() {
// runZonedGuarded capturează ORICE eroare asincronă neprinsă din interiorul blocului:
runZonedGuarded(
() {
print('Aplicația a pornit în interiorul zonei securizate.');
// Excepție asincronă declanșată într-un Timer:
Timer(const Duration(milliseconds: 100), () {
throw StateError('Eroare asincronă fatale din timer!');
});
// Viitor asincron eșuat:
Future.error(Exception('Eroare din Future netratat!'));
},
(error, stackTrace) {
// Handler centralizat de erori (ex: trimitere către Sentry / Crashlytics):
print('[CRASH REPORTER] Eroare capturată în Zonă: $error');
// Aplicația rămâne activă și nu se prăbușește!
},
zoneSpecification: ZoneSpecification(
// Interceptarea tuturor apelurilor print():
print: (self, parent, zone, line) {
final timestamp = DateTime.now().toIso8601String();
parent.print(zone, '[$timestamp] $line');
},
),
);
}
Tabel Tehnic: Zone Values vs Variabile Globale
| Criteriu | Zone Values (zoneValues: {#key: value}) |
Variabile Globale Tradiționale |
|---|---|---|
| Izolare Asincronă | Fiecare lanț de apeluri asincrone își păstrează propriul context neatins. | Pot fi suprascrise concurent de alte cereri, producând bug-uri de stare. |
| Propagare | Se transmit automat către toate microtask-urile și viitoarele create în zonă. | Necesită transmitere manuală prin parametri de funcție. |
| Imutabilitate | Strict imutabile în cadrul aceleiași zone. | Mutabile și predispuse la modificări accidentale. |
| Cazuri Recomandate | Corelare de cereri HTTP (requestId, traceId), token-uri de autentificare per sesiune. |
Configurații statice la pornirea aplicației. |
import 'dart:async';
const Symbol requestContextKey = #requestId;
void logWithContext(String message) {
// Preluăm valoarea injectată în zona curentă:
final traceId = Zone.current[requestContextKey] ?? 'FĂRĂ-TRACE';
print('[$traceId] $message');
}
Future handleRequest(String reqId) async {
runZoned(
() async {
logWithContext('Procesare pasul 1');
await Future.delayed(const Duration(milliseconds: 50));
logWithContext('Procesare pasul 2 finalizată');
},
zoneValues: {requestContextKey: reqId},
);
}
void main() {
handleRequest('REQ-901');
handleRequest('REQ-902');
}