← Назад к вопросам

Для чего нужны изоляты?

1.7 Middle🔥 231 комментариев
#Асинхронность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Isolates в Dart и Flutter

Isolate — это концепция из Dart для параллельного выполнения кода. В отличие от других языков, где используются потоки, Dart использует изоляты для настоящего параллелизма благодаря отсутствию shared-state concurrency.

Что такое Isolate

Isolate (изолят) — это независимый работник, который выполняет код параллельно с основным потоком. Каждый изолят имеет:

  • Собственный event loop — обрабатывает асинхронные события
  • Собственную память — не делится с другими изолятами
  • Собственный Dart runtime — полная независимость
Основной thread (UI)     │     Isolate #1       │     Isolate #2
─────────────────────────┼──────────────────────┼──────────────
build()                  │                      │
setState()               │ Heavy computation    │ Heavy computation
renderFrame()            │ (не блокирует UI)    │ (не блокирует UI)

Почему нужны Isolates

Проблема: Dart is single-threaded

В отличие от многих языков, Dart выполняет весь код в одном потоке. Если одна операция занимает долго, весь UI замораживается:

// ❌ Плохо: блокирует UI
Future<void> fetchAndProcess() async {
  final data = await api.fetchHeavyData(); // OK, async
  
  final processed = processData(data); // ❌ БЛОКИРУЕТ! Если 5 сек
  
  setState(() => result = processed);
}

// processData() выполняется синхронно и напрямую блокирует UI
List<int> processData(List<int> data) {
  var result = [];
  for (int i = 0; i < 10000000; i++) {
    result.add(expensiveCalculation(data[i % data.length]));
  }
  return result; // 5 секунд! UI зависает!
}

Решение: Isolates

// ✅ Хорошо: используем изолят
Future<void> fetchAndProcess() async {
  final data = await api.fetchHeavyData();
  
  // Выполняй heavy работу в отдельном изоляте
  final processed = await Isolate.run(() => processData(data));
  
  setState(() => result = processed);
}

// processData выполняется в отдельном потоке, UI остается responsive
List<int> processData(List<int> data) {
  var result = [];
  for (int i = 0; i < 10000000; i++) {
    result.add(expensiveCalculation(data[i % data.length]));
  }
  return result; // 5 секунд, но UI не зависает!
}

Практические примеры

Пример 1: Обработка больших объемов данных

import 'dart:isolate';

Future<List<String>> processLargeFile(File file) async {
  // Читаем файл в основном потоке (быстро)
  final content = await file.readAsString();
  
  // Обрабатываем в отдельном изоляте (тяжело)
  return await Isolate.run(() {
    return content
        .split('\n')
        .map((line) => line.toUpperCase())
        .where((line) => line.isNotEmpty)
        .toList();
  });
}

// Использование
var lines = await processLargeFile(myFile);
setState(() => displayLines = lines);

Пример 2: JSON парсинг

Future<List<User>> parseJsonUsers(String jsonString) {
  // JSON парсинг может быть медленным на больших объемах
  return Isolate.run(() {
    final jsonData = jsonDecode(jsonString) as List;
    return jsonData
        .map((item) => User.fromJson(item))
        .toList();
  });
}

// Использование
Final users = await parseJsonUsers(jsonResponse);

Пример 3: Криптографические операции

Future<String> encryptData(String plainText, String key) {
  return Isolate.run(() {
    // Шифрование может быть очень медленным
    final cipher = Cipher.aes(
      plainText,
      key,
      encryptionMode: EncryptionMode.cbc,
    );
    return cipher.toBase64();
  });
}

Пример 4: Долгоживущий isolate (передача сообщений)

class ImageProcessor {
  late Isolate _isolate;
  late SendPort _sendPort;
  
  Future<void> initialize() async {
    final receivePort = ReceivePort();
    _isolate = await Isolate.spawn(
      _imageProcessingEntryPoint,
      receivePort.sendPort,
    );
    
    _sendPort = await receivePort.first;
  }
  
  static void _imageProcessingEntryPoint(SendPort mainSendPort) {
    final port = ReceivePort();
    mainSendPort.send(port.sendPort);
    
    port.listen((message) {
      if (message is String) {
        final result = heavyImageProcessing(message);
        message.reply(result);
      }
    });
  }
  
  Future<String> process(String imagePath) async {
    final response = ReceivePort();
    _sendPort.send([response.sendPort, imagePath]);
    return await response.first;
  }
  
  void dispose() {
    _isolate.kill();
  }
}

Quando использовать Isolates

Используй Isolates для:

  • JSON парсинг больших объемов
  • Обработка изображений (ресайз, фильтры, кроп)
  • Криптография (шифрование, хеширование)
  • Вычисления (ML модели, большие матрицы)
  • Работа с файлами (сжатие, конвертация)
  • Database операции (сложные запросы)
// Пример: обработка множество изображений
Future<List<Image>> processImages(List<String> imagePaths) {
  return Isolate.run(() {
    return imagePaths
        .map((path) => _processImage(path))
        .toList();
  });
}

НЕ используй Isolates для:

  • Быстрые операции (<50ms) — overhead изолята замедлит
  • UI обновления — UI работает только в main isolate
  • Сетевые запросы — используй async/await
  • Простые вычисления — overhead не оправдает
// ❌ Неправильно: Isolate для простого вычисления
await Isolate.run(() => 2 + 2); // Overhead > результат

// ✅ Правильно: async/await для сети
final data = await http.get(url);

Производительность

Время создания Isolate:

Основной isolate (main):       0 ms (already running)
Создание нового isolate:       50-200 ms (зависит от устройства)
Отправка сообщения:           <1 ms
Получение ответа:             <1 ms

Практический чек-лист:

Операция займет:  50ms  →  500ms  →  5000ms
─────────────────  ───────────────────────
Лучше async/await  Рассмотрим isolate  Обязательно isolate

Лучшие практики

  1. Используй Isolate.run() для простых случаев

    final result = await Isolate.run(() => heavyWork());
    
  2. Используй долгоживущие isolates для повторяющихся операций

    // Если нужно обрабатывать 1000+ изображений
    final processor = ImageProcessor();
    await processor.initialize();
    
    for (var path in paths) {
      final result = await processor.process(path);
    }
    
  3. Всегда обрабатывай ошибки

    try {
      final result = await Isolate.run(() => riskyWork());
    } catch (e) {
      print('Isolate error: $e');
    }
    
  4. Не передавай большие объекты

    // ❌ Плохо: копирование больших данных
    await Isolate.run(() => process(hugeList)); // Медленно
    
    // ✅ Хорошо: передай только необходимое
    await Isolate.run(() => process(hugeList.sublist(0, 100)));
    

Альтернативы в Flutter

  • compute() функция — обёртка над Isolate.run() для FlutterDelegate

    import 'package:flutter/foundation.dart';
    final result = await compute(heavyWork, param);
    
  • Background Isolate с методом каналов для интеграции с платформой (iOS/Android)

Isolates — это мощный инструмент для обработки heavy work без блокирования UI. Но помни: overhead создания изолята должен быть меньше выигрыша от параллельного выполнения.

Для чего нужны изоляты? | PrepBro