Zone de Execuție în Dart: Rulare în Sandbox, Interceptarea Erorilor Asincrone Neinterceptate și Context Tracing în Dart

Ghid practic pentru Zone în Dart: crearea de sandbox-uri de execuție cu runZonedGuarded, interceptarea erorilor asincrone fatale nesurprinse de try/catch, injectarea de valori de context și suprascrierea comportamentului standard de print/timere.

Sintaxă
runZonedGuarded(() => runApp(), (error, stack) => crashReporter.record(error, stack));

Î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.

Interceptarea Erorilor Globale cu runZonedGuarded
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.
Injectarea Valorilor de Context (Zone Values)
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');
}