Код-ревью Senior Dart: Future, Stream, изоляты обновлено

Код-ревью: асинхронный сервис синхронизации

Вопрос

Сервис синхронизации заметок. Найдите проблемы с асинхронностью, ошибками и ресурсами.

class SyncService {
  final _controller = StreamController<int>();
  Stream<int> get progress => _controller.stream;

  Future<void> syncAll(List<Note> notes) async {
    notes.forEach((n) async {
      await api.upload(n);
    });
    final parsed = jsonDecode(await api.downloadAll());
    for (final n in parsed) {
      await db.save(Note.fromJson(n));
      _controller.add(1);
    }
  }

  void start() {
    syncAll(localNotes);
  }
}

Коротко

Правильная версия: загрузка через Future.wait с ограничением параллельности, парсинг в изоляте, пакетная запись, флаг или Completer, не дающий запустить синхронизацию дважды, явная обработка ошибок и dispose с закрытием контроллера. Прогресс через broadcast-поток или ValueNotifier.

Что должен найти ревьюер

  1. Ошибкастрока 2
    Single-subscription контроллер, а прогресс слушают несколько экранов: вторая подписка бросит StateError. Нужен broadcast или отдельный поток на каждого слушателя.
  2. Ошибкастрока 6
    forEach с async-колбэком не ждёт загрузку: syncAll идёт дальше, ошибки загрузки становятся необработанными.
  3. Производительностьстрока 9
    jsonDecode большого ответа в UI-изоляте блокирует кадры. Вынести в Isolate.run или compute.
  4. Производительностьстрока 11
    Сохранение по одной записи с await в цикле: медленно. Пакетная запись в транзакции.
  5. Ошибкастрока 17
    syncAll без await и обработки ошибки: исключение станет необработанной ошибкой зоны, а повторный start запустит параллельную синхронизацию.
  6. Ошибкастрока 2
    Контроллер никогда не закрывается: нужен dispose сервиса с _controller.close().

Разбор

Правильная версия: загрузка через Future.wait с ограничением параллельности, парсинг в изоляте, пакетная запись, флаг или Completer, не дающий запустить синхронизацию дважды, явная обработка ошибок и dispose с закрытием контроллера. Прогресс через broadcast-поток или ValueNotifier.

Ответьте сами, а не прочитайте

В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.