Dart Isolates e Concorrência em 2026: compute, Async e Perguntas de Entrevista

Guia completo sobre Dart Isolates, a função compute e concorrência no Flutter 2026. Aprenda as melhores práticas e prepare-se para entrevistas técnicas.

Dart Isolates e Concorrência em 2026: compute, Async e Perguntas de Entrevista

A programação concorrente representa um pilar fundamental no desenvolvimento de aplicações Flutter de alto desempenho. Em 2026, os Dart Isolates constituem a solução preferida para executar operações custosas sem bloquear a interface do usuário. Este artigo explora em profundidade os mecanismos de concorrência do Dart, a função compute e as perguntas mais frequentes em entrevistas técnicas.

Os Isolates do Dart funcionam de maneira diferente das threads tradicionais: cada Isolate possui sua própria memória e se comunica exclusivamente por meio de mensagens. Essa arquitetura elimina os problemas de dados compartilhados e condições de corrida.

Compreendendo o Modelo de Concorrência do Dart

O Dart utiliza um modelo de concorrência baseado em Isolates, onde cada Isolate possui seu próprio heap de memória isolado. Diferentemente das threads que compartilham memória, os Isolates se comunicam unicamente por mensagens, garantindo assim segurança de memória por design.

O event loop do Dart gerencia dois tipos de filas: a microtask queue e a event queue. As microtasks têm prioridade sobre os events, o que permite um controle preciso do ordenamento das operações assíncronas.

dart
import 'dart:async';

void main() {
  print('1. Main starts');
  
  Future(() => print('4. Event queue'));
  
  scheduleMicrotask(() => print('3. Microtask queue'));
  
  print('2. Main ends');
}
// Output: 1, 2, 3, 4

A Função compute: Simplicidade e Performance

A função compute do Flutter oferece uma abstração elegante para executar tarefas pesadas em um Isolate separado. Ela gerencia automaticamente a criação do Isolate, a transferência de dados e a recuperação do resultado.

dart
import 'package:flutter/foundation.dart';

Future<List<int>> processLargeData(List<int> data) async {
  return await compute(_heavyComputation, data);
}

List<int> _heavyComputation(List<int> data) {
  // Esta função é executada em um Isolate separado
  return data.map((e) => e * e).where((e) => e > 100).toList();
}

A função compute é perfeita para operações pontuais. Para comunicações bidirecionais ou tarefas recorrentes, os Isolates manuais oferecem maior flexibilidade.

Criação Manual de Isolates

Os Isolates manuais permitem comunicação contínua entre o Isolate principal e os Isolates secundários por meio de portas de comunicação.

dart
import 'dart:isolate';

Future<void> runIsolate() async {
  final receivePort = ReceivePort();
  
  await Isolate.spawn(
    isolateEntryPoint,
    receivePort.sendPort,
  );
  
  final sendPort = await receivePort.first as SendPort;
  
  final responsePort = ReceivePort();
  sendPort.send(['process', [1, 2, 3, 4, 5], responsePort.sendPort]);
  
  final result = await responsePort.first;
  print('Result: $result');
}

void isolateEntryPoint(SendPort mainSendPort) {
  final receivePort = ReceivePort();
  mainSendPort.send(receivePort.sendPort);
  
  receivePort.listen((message) {
    final command = message[0] as String;
    final data = message[1] as List<int>;
    final replyPort = message[2] as SendPort;
    
    if (command == 'process') {
      final result = data.map((e) => e * 2).toList();
      replyPort.send(result);
    }
  });
}

Isolate.run: A Nova API Simplificada

Desde o Dart 2.19, Isolate.run oferece uma sintaxe mais concisa para operações isoladas simples, com gerenciamento automático de closures.

dart
import 'dart:isolate';

Future<int> computeSum(List<int> numbers) async {
  return await Isolate.run(() {
    return numbers.reduce((a, b) => a + b);
  });
}

Future<Map<String, dynamic>> parseJson(String jsonString) async {
  return await Isolate.run(() {
    return jsonDecode(jsonString) as Map<String, dynamic>;
  });
}

Tratamento de Erros em Isolates

O tratamento de erros em Isolates requer atenção especial, pois as exceções não se propagam automaticamente para o Isolate principal.

dart
import 'dart:isolate';

Future<T> runWithErrorHandling<T>(Future<T> Function() computation) async {
  try {
    return await Isolate.run(computation);
  } on IsolateSpawnException catch (e) {
    throw Exception('Failed to spawn isolate: $e');
  } catch (e) {
    throw Exception('Isolate computation failed: $e');
  }
}

Future<void> isolateWithErrorPort() async {
  final receivePort = ReceivePort();
  final errorPort = ReceivePort();
  
  await Isolate.spawn(
    riskyOperation,
    receivePort.sendPort,
    onError: errorPort.sendPort,
  );
  
  errorPort.listen((error) {
    print('Isolate error: $error');
  });
}

void riskyOperation(SendPort sendPort) {
  throw Exception('Something went wrong');
}

Pools de Isolates para Performance

Para aplicações que requerem múltiplas operações paralelas, um pool de Isolates evita o custo de criação repetida.

dart
import 'dart:async';
import 'dart:isolate';

class IsolatePool {
  final int size;
  final List<Isolate> _isolates = [];
  final List<SendPort> _sendPorts = [];
  int _currentIndex = 0;
  
  IsolatePool({this.size = 4});
  
  Future<void> initialize() async {
    for (var i = 0; i < size; i++) {
      final receivePort = ReceivePort();
      final isolate = await Isolate.spawn(
        _workerEntryPoint,
        receivePort.sendPort,
      );
      _isolates.add(isolate);
      _sendPorts.add(await receivePort.first as SendPort);
    }
  }
  
  Future<R> execute<T, R>(R Function(T) task, T argument) async {
    final responsePort = ReceivePort();
    final sendPort = _sendPorts[_currentIndex];
    _currentIndex = (_currentIndex + 1) % size;
    
    sendPort.send([task, argument, responsePort.sendPort]);
    return await responsePort.first as R;
  }
  
  void dispose() {
    for (final isolate in _isolates) {
      isolate.kill();
    }
  }
  
  static void _workerEntryPoint(SendPort mainSendPort) {
    final receivePort = ReceivePort();
    mainSendPort.send(receivePort.sendPort);
    
    receivePort.listen((message) {
      final task = message[0] as Function;
      final argument = message[1];
      final replyPort = message[2] as SendPort;
      
      final result = task(argument);
      replyPort.send(result);
    });
  }
}

Async/Await e Streams

As operações assíncronas no Dart utilizam async/await para uma sintaxe legível e os Streams para fluxos de dados contínuos.

dart
Stream<int> numberStream() async* {
  for (var i = 1; i <= 5; i++) {
    await Future.delayed(Duration(seconds: 1));
    yield i;
  }
}

Future<void> processStream() async {
  await for (final number in numberStream()) {
    print('Received: $number');
  }
}

Stream<int> transformedStream() {
  return numberStream()
      .where((n) => n.isEven)
      .map((n) => n * 10);
}

Perguntas Frequentes em Entrevistas

As entrevistas técnicas de Flutter abordam regularmente a concorrência. A seguir estão as perguntas mais comuns com suas respostas.

Pergunta 1: Qual é a diferença entre Isolates e Threads?

Os Isolates possuem cada um sua própria memória heap e se comunicam por mensagens, enquanto as threads compartilham memória. Esse isolamento garante a ausência de race conditions, mas implica um custo de serialização de dados.

Pergunta 2: Quando usar compute versus Isolate.spawn?

A função compute é adequada para operações pontuais sem comunicação bidirecional. Isolate.spawn oferece maior controle para comunicações contínuas ou tarefas complexas.

dart
// compute para uma operação simples
final result = await compute(parseJson, jsonString);

// Isolate.spawn para comunicação bidirecional
final receivePort = ReceivePort();
await Isolate.spawn(workerFunction, receivePort.sendPort);

Pergunta 3: Como compartilhar dados entre Isolates?

Os dados são copiados durante a transferência entre Isolates. Para grandes quantidades de dados, TransferableTypedData permite transferência sem cópia.

dart
import 'dart:typed_data';

void transferLargeData(SendPort sendPort) {
  final largeList = Uint8List(1000000);
  final transferable = TransferableTypedData.fromList([largeList]);
  sendPort.send(transferable);
}

Pronto para mandar bem nas entrevistas de Flutter?

Pratique com nossos simuladores interativos, flashcards e testes tecnicos.

Melhores Práticas e Otimizações

O uso eficiente de Isolates requer compreender os trade-offs entre performance e complexidade.

O custo de criar um Isolate justifica seu uso apenas para operações que excedam alguns milissegundos. Para cálculos rápidos, o overhead de serialização anula os ganhos de performance.

A serialização de objetos complexos pode se tornar um gargalo. Os tipos primitivos e as coleções simples são transferidos eficientemente, diferentemente dos objetos com referências circulares.

dart
// Correto: tipos primitivos e coleções simples
await compute(processInts, [1, 2, 3, 4, 5]);

// Evitar: objetos complexos com dependências
// await compute(processWidget, myWidget); // Erro!

Conclusão

Os Dart Isolates e a programação assíncrona constituem competências essenciais para qualquer desenvolvedor Flutter em 2026. O domínio de compute, Isolate.run e dos padrões de comunicação entre Isolates permite criar aplicações responsivas mesmo durante processamentos intensivos. Os conceitos apresentados neste artigo preparam eficazmente para as perguntas de entrevista técnica enquanto fornecem padrões aplicáveis imediatamente em produção.

Compartilhar

Artigos relacionados