Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Lazy в Dart: Ленивое вычисление и инициализация
Lazy — это концепция ленивого вычисления, при которой код или данные инициализируются только в момент их фактического использования, а не при объявлении. Это мощный инструмент для оптимизации памяти и производительности.
Основной концепт
Eager vs Lazy вычисление:
// Eager — вычисляется сразу
final result = computeExpensive(); // Выполняется сейчас!
// Lazy — отложенное вычисление
final Function result = () => computeExpensive(); // Выполнится позже
Зачем это нужно:
- Экономия памяти (не всё вычисляем, если не используем)
- Повышение начальной производительности приложения
- Избежание ошибок при инициализации неиспользуемых данных
Lazy переменные с late ключевым словом
late параметр:
class DatabaseConnection {
// Переменная объявлена, но инициализируется позже
late Database _db;
DatabaseConnection();
Future<void> initialize() async {
_db = await Database.open();
}
void query(String sql) {
_db.execute(sql); // Используем инициализированный объект
}
}
Ошибка при раннем использовании:
class User {
late String name;
// Ошибка: LateInitializationError!
void printName() => print(name); // Если name не инициализирован
}
Lazy инициализация в Singleton'е
Классический паттерн:
class ApiService {
static final ApiService _instance = ApiService._internal();
factory ApiService() {
return _instance;
}
ApiService._internal();
late String _authToken;
late http.Client _client;
Future<void> initialize(String token) async {
_authToken = token;
_client = http.Client();
}
Future<String> fetchData() async {
return await _client.get(_buildUrl()).then((response) => response.body);
}
}
// Использование
final api = ApiService();
await api.initialize(token);
Lazy в коллекциях
Iterable lazy операции:
// Eager — вычисляет сразу все элементы
final List<int> eager = [1, 2, 3, 4, 5]
.map((x) => x * 2)
.toList(); // Создаёт новый список
// Lazy — вычисляет элементы по требованию
final Iterable<int> lazy = [1, 2, 3, 4, 5]
.map((x) => x * 2);
// .toList() не вызван, элементы не вычислены!
// Элементы вычисляются только при итерации
for (final value in lazy) {
print(value); // Здесь вычисляется каждый элемент
}
Практический пример:
class DataProcessor {
Iterable<User> filterAndMap(List<Map<String, dynamic>> data) {
return data
.where((item) => item['age'] > 18) // Lazy фильтр
.map((item) => User.fromJson(item)) // Lazy маппинг
.where((user) => user.isActive);
// Ничего не вычислено!
}
}
// Вычисление происходит здесь
final users = processor.filterAndMap(data).toList();
Lazy с Future'ами и Stream'ами
Lazy Future:
final Future<String> eagerFuture = fetchDataFromApi(); // Запрос идёт сразу!
// Lazy функция, возвращающая Future
Future<String> lazyFetch() => fetchDataFromApi(); // Запрос когда вызовут функцию
// Использование
await lazyFetch(); // Запрос происходит здесь
Lazy Stream:
Stream<int> createCounterStream() {
// Генератор не создан, пока никто не подписан
return Stream.periodic(Duration(seconds: 1), (count) => count);
}
// Stream создаётся только при подписке
final subscription = createCounterStream().listen((count) => print(count));
Lazy инициализация в StatefulWidget
Правильный подход:
class MyHomePage extends StatefulWidget {
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late List<String> items; // Инициализируется в initState
late StreamSubscription<String> subscription;
@override
void initState() {
super.initState();
items = []; // Lazy инициализация
subscription = Stream.fromIterable(['a', 'b', 'c']).listen((item) {
setState(() => items.add(item));
});
}
@override
void dispose() {
subscription.cancel(); // Очистка!
super.dispose();
}
@override
Widget build(BuildContext context) {
return ListView(
children: items.map((item) => Text(item)).toList(),
);
}
}
GetIt для lazy сервисов
Dependency Injection с ленивой инициализацией:
final getIt = GetIt.instance;
void setupServiceLocator() {
// Lazy singleton — инициализируется при первом обращении
getIt.registerLazySingleton<ApiService>(
() => ApiService(),
);
// Фабрика — создаёт новый экземпляр каждый раз
getIt.registerFactory<Repository>(
() => Repository(getIt<ApiService>()),
);
}
// Использование
final api = getIt<ApiService>(); // Создаётся в первый раз
final api2 = getIt<ApiService>(); // Возвращается тот же экземпляр
Лучшие практики
1. Избегайте LateInitializationError:
class Config {
late final String apiUrl;
void configure(String url) {
apiUrl = url; // Инициализация
}
}
// Добавьте проверку
bool get isInitialized => apiUrl != null;
2. Используйте final с late для иммутабельности:
class Service {
late final String token; // Может быть присвоено один раз
// token = 'new'; // Ошибка!
}
3. Применяйте lazy для коллекций:
// Хорошо — не копируем весь список
final filtered = largeList.where((item) => item.isValid);
// Плохо — создаём новый список
final filtered = largeList.where((item) => item.isValid).toList();
Mastering lazy инициализацию — признак опытного разработчика Flutter, позволяющий писать более эффективный и отзывчивый код.