Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Передача класса в изолят
Отличный вопрос о одной из самых сложных особенностей Dart изолятов. Коротко: НЕТ, нельзя передать класс как есть. Давайте разберемся почему и как это обойти.
Основная проблема
Дартовский изолят — это полностью независимый поток выполнения с отдельной памятью и Garbage Collector. Классы (объекты) хранятся в памяти и имеют состояние, которое нельзя просто так скопировать между изолятами.
// Неправильно: так не работает!
class User {
final String name;
User(this.name);
}
void main() async {
final user = User('John');
final receivePort = ReceivePort();
// ОШИБКА: нельзя передать объект класса!
await Isolate.spawn(
_isolateTask,
user, // Это не сработает!
);
}
void _isolateTask(User user) {
print(user.name);
}
Результат: RuntimeError - нельзя сериализовать произвольные классы между изолятами.
Что можно передавать в изолят
1. Примитивные типы (работают всегда)
void main() async {
final receivePort = ReceivePort();
await Isolate.spawn(
_isolateTask,
{
'name': 'John', // String ✅
'age': 30, // int ✅
'salary': 50000.5, // double ✅
'active': true, // bool ✅
'data': [1, 2, 3], // List ✅
'map': {'key': 'value'}, // Map ✅
},
);
}
void _isolateTask(Map<String, dynamic> params) {
print(params['name']);
print(params['age']);
}
2. Класс с аннотацией @pragma('vm:entry-point')
Для классов, которые должны быть доступны в изолятах:
@pragma('vm:entry-point')
class User {
final String name;
final int age;
User({required this.name, required this.age});
// Метод для сериализации
Map<String, dynamic> toMap() {
return {'name': name, 'age': age};
}
// Фабрика для десериализации
factory User.fromMap(Map<String, dynamic> map) {
return User(
name: map['name'] as String,
age: map['age'] as int,
);
}
}
void main() async {
final user = User(name: 'John', age: 30);
final receivePort = ReceivePort();
// Передаём сериализованные данные
await Isolate.spawn(
_isolateTask,
user.toMap(), // Передаём Map, не объект!
);
}
void _isolateTask(Map<String, dynamic> userData) {
final user = User.fromMap(userData);
print('User: ${user.name}, Age: ${user.age}');
}
3. SendPort для двусторонней коммуникации
Это способ передачи порта между изолятами:
void main() async {
final receivePort = ReceivePort();
// Передаём SendPort для ответа
await Isolate.spawn(
_isolateTask,
receivePort.sendPort, // SendPort ✅
);
// Слушаем ответ
receivePort.listen((message) {
print('Ответ из изолята: $message');
});
}
void _isolateTask(SendPort sendPort) {
// Отправляем результат назад
sendPort.send('Hello from isolate');
}
Правильный способ: Паттерн Request-Response
Это наиболее эффективный подход для сложных задач:
class UserService {
final String name;
final String email;
UserService({required this.name, required this.email});
// Сериализуем для передачи
Map<String, dynamic> toMap() {
return {'name': name, 'email': email};
}
factory UserService.fromMap(Map<String, dynamic> map) {
return UserService(
name: map['name'],
email: map['email'],
);
}
}
void main() async {
final receivePort = ReceivePort();
final userData = {
'user': UserService(name: 'John', email: 'john@example.com').toMap(),
'sendPort': receivePort.sendPort,
};
await Isolate.spawn(_isolateTask, userData);
// Получаем результат
final result = await receivePort.first;
print('Результат: $result');
}
void _isolateTask(Map<String, dynamic> params) {
final sendPort = params['sendPort'] as SendPort;
final userData = params['user'] as Map<String, dynamic>;
final user = UserService.fromMap(userData);
// Обработка в изолате
final result = 'Обработал ${user.name}';
// Отправляем ответ
sendPort.send(result);
}
Современный подход: compute() функция
Для простых задач лучше использовать compute() из flutter:
import 'package:flutter/foundation.dart';
// Функция должна быть top-level или static
Future<String> expensiveComputation(Map<String, dynamic> params) async {
final name = params['name'] as String;
// Тяжелая работа
await Future.delayed(Duration(seconds: 2));
return 'Processed: $name';
}
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () async {
// Простой способ использовать изолят
final result = await compute(
expensiveComputation,
{'name': 'John'},
);
print(result);
},
child: Text('Run Computation'),
);
}
}
Преимущества compute():
- Автоматически создает и управляет изолятом
- Простой синтаксис
- Нет нужды вручную работать с SendPort/ReceivePort
- Идеально для обработки данных
Практический пример: Обработка JSON в изолате
class ApiResponse {
final int statusCode;
final String body;
ApiResponse(this.statusCode, this.body);
Map<String, dynamic> toMap() => {
'statusCode': statusCode,
'body': body,
};
factory ApiResponse.fromMap(Map<String, dynamic> map) {
return ApiResponse(map['statusCode'], map['body']);
}
}
// Тяжелая работа в изолате
Future<Map<String, dynamic>> parseJsonInIsolate(
Map<String, dynamic> params,
) async {
final response = ApiResponse.fromMap(params['response']);
// Парсим большой JSON
final jsonData = jsonDecode(response.body);
// Сложная обработка
final processed = jsonData.map((item) => {
'id': item['id'],
'name': item['name'],
'processed': true,
}).toList();
return {'success': true, 'data': processed};
}
future<void> main() async {
final response = ApiResponse(200, jsonEncode([
{'id': 1, 'name': 'John'},
{'id': 2, 'name': 'Jane'},
]));
final result = await compute(
parseJsonInIsolate,
{'response': response.toMap()},
);
print(result);
}
Сравнение подходов
| Подход | Сложность | Производительность | Рекомендуется для |
|---|---|---|---|
| Примитивные типы | Низкая | Отличная | Простые данные |
| compute() | Низкая | Хорошая | Обработка данных |
| SendPort/ReceivePort | Средняя | Отличная | Двусторонняя коммуникация |
| Serializable классы | Средняя | Хорошая | Сложные объекты |
Key Takeaways
✅ Передавайте:
- Примитивные типы (int, String, bool, double)
- List и Map с примитивными значениями
- SendPort для коммуникации
- Сериализованные данные (toMap(), JSON)
❌ НЕ передавайте:
- Объекты произвольных классов
- Функции и closures
- Контексты (BuildContext, DatabaseConnection)
- Файловые дескрипторы
Правило золотого стандарта: Передавайте в изолят простые типы, сериализуйте сложные объекты в Map/JSON, используйте SendPort для ответов.