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

Что такое lazy в Dart?

1.0 Junior🔥 132 комментариев
#Dart

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

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

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

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, позволяющий писать более эффективный и отзывчивый код.