Irbisa · Шпаргалка

Асинхронность и Future

Future, async/await, then/catchError, FutureBuilder

Junior-разработчик · 22 вопроса

  1. 01

    Что такое Future, как работают async и await и как обрабатывать ошибки Future?

    Future<T> представляет значение или ошибку, которые придут позже.

    Future<String> fetchUser(int id) async {  await Future<void>.delayed(const Duration(seconds: 1)); // stands in for a network call  if (id <= 0) throw ArgumentError.value(id, 'id');  return 'User #$id';}…
  2. 02

    Выполняет ли async/await ваш код в другом потоке?

    Нет. await лишь позволяет event loop заняться другой работой, пока ожидается что-то вне вашего изолята: сетевой запрос, чтение файла, таймер.

  3. 03

    Что выведет этот код и какая часть load() выполнится до того, как main() пойдёт дальше?

    Future<void> load() async {  print('2');  await Future<void>.delayed(Duration.zero);  print('4');} void main() {  print('1');  load();  print('3');}

    Он выведет 1 2 3 4.

  4. 04

    Чем цепочка .then() отличается от async/await и чем whenComplete() отличается от then()?

    Оба способа строят один и тот же Future, так что это вопрос читаемости, и async/await обычно выигрывает.

    // then-chainFuture<Profile> loadChain(String id) {  return api      .user(id)      .then((u) => api.profile(u.id))      .catchError((Object e) => Profile.empty());…
  5. 05

    Что такое Timer / Timer.periodic, как его отменить и что будет, если забыть отменить его в dispose()?

    Timer(duration, callback) вызывает колбэк один раз после задержки, а Timer.periodic вызывает его многократно; оба возвращают Timer, который останавливают через cancel().

    class _ClockState extends State<Clock> {  Timer? _ticker;  Timer? _debounce;  DateTime _now = DateTime.now();   @override…
  6. 06

    Что делает FutureBuilder и почему его Future нельзя создавать внутри build()?

    FutureBuilder перестраивает часть UI по состоянию Future: ожидание, завершён с данными или завершён с ошибкой.

    class ProfilePage extends StatefulWidget {  const ProfilePage({super.key, required this.id});  final String id;   @override  State<ProfilePage> createState() => _ProfilePageState();…
  7. 07

    Почему async-функция должна возвращать Future<void>, а не void?

    Потому что с void вызывающему нечего ожидать: он не может дождаться окончания работы, а ошибки изнутри минуют его try/catch и всплывают как необработанные.

    // wrong: callers cannot wait for it or catch its errorsvoid saveDraftVoid() async {  await storage.write(draft);} // right…
  8. 08

    Что будет, если забыть await у Future, и когда допустим fire-and-forget через unawaited()?

    Функция идёт дальше, не дождавшись окончания работы, а у ошибки этого Future нет слушателя, поэтому она становится необработанной асинхронной ошибкой и не доходит до вашего try/catch.

    Future<void> onSave() async {  await repo.save(draft); // right: wait for what the next line depends on  unawaited(analytics.track("saved").catchError(logDrop)); // fire-and-forget, errors handled  // wrong: repo.save(draft); close(); // close() runs before the save finishes}
  9. 09

    Найдите баг: тост «All photos synced» появляется, пока фото ещё загружаются.

    Future<void> syncPhotos(List<Photo> photos) async {  photos.forEach((photo) async {    await api.upload(photo);    await db.markSynced(photo.id);  });  showToast('All ${photos.length} photos synced');}

    forEach игнорирует то, что возвращает колбэк, поэтому Future async-замыкания теряются: syncPhotos показывает тост и завершается раньше, чем закончится хоть одна загрузка, а упавшая загрузка становится неперехваченной ошибкой вместо того, чтобы дойти до вызывающего.

  10. 10

    Как запустить несколько Future одновременно и что происходит, если один из них падает?

    Сначала запустите все Future, а затем ждите их вместе: Future.wait([...]) или, в Dart 3, через запись (record), например await (fetchUser(), fetchPrefs()).wait.

    Future<void> load(String id) async {  // Same type: one list, results in the original order  final pages = await Future.wait([loadPage(1), loadPage(2), loadPage(3)]);   // Per-future fallback: one failure does not lose the other result  final counts = await Future.wait([…
  11. 11

    Как работает event loop в Dart и чем очередь микрозадач отличается от очереди событий?

    Каждый изолят работает в одном потоке с event loop и двумя очередями.

  12. 12

    В каком порядке выведет строки этот код и почему?

    void main() {  print('A');  Future(() => print('B'));  Future.delayed(Duration.zero, () => print('C'));  scheduleMicrotask(() => print('D'));  Future.value(1).then((_) => print('E'));  Future.sync(() => print('F'));  print('G');}

    A F G D E B C.

  13. 13

    Что такое Completer и когда его использовать?

    Completer<T> создаёт Future, который вы завершаете вручную: отдаёте наружу completer.future, а когда придёт результат, вызываете complete(value) или completeError(error, stackTrace).

    Future<Position> currentPosition(LocationSdk sdk) {  final completer = Completer<Position>();   sdk.requestOnce(    onSuccess: (pos) {      if (!completer.isCompleted) completer.complete(pos);…
  14. 14

    Что такое FutureOr<T> и когда функция возвращает его вместо Future<T>?

    Тип FutureOr<T> означает «либо T, либо Future<T>».

    FutureOr<User> getUser(String id) {  final cached = _cache[id];  if (cached != null) return cached;   // synchronous: no event-loop turn  return _fetchAndCache(id);           // Future<User>}…
  15. 15

    Почему catchError здесь не срабатывает при пустом id и как это исправить?

    Future<User> load(String id) {  if (id.isEmpty) throw ArgumentError.value(id, 'id');  return api.getUser(id);} void onTap(String id) {  load(id).catchError((Object e) {    log('failed: $e');    return User.guest();  });}

    load не помечена async, поэтому throw происходит на стеке вызывающего ещё до того, как появился какой-либо Future.

  16. 16

    Найдите баг: без сети loadProfile бросает исключение вместо того, чтобы вернуть профиль из кэша.

    Future<Profile> loadProfile(String id) async {  try {    return api.fetchProfile(id);  } on NetworkException {    return cache.read(id);  }}

    return api.fetchProfile(id) возвращает Future без await, поэтому выход из блока try происходит раньше, чем запрос упадёт.

  17. 17

    Как добавить к Future таймаут и повторные попытки?

    future.timeout(duration) возвращает новый Future, который падает с TimeoutException, если исходный выполняется слишком долго, или возвращает фолбэк из onTimeout.

    Future<T> retry<T>(  Future<T> Function() task, {  int maxAttempts = 4,  Duration baseDelay = const Duration(milliseconds: 500),  bool Function(Object error) retryIf = _isTransient,}) async {…
  18. 18

    Как перехватить все необработанные ошибки во Flutter-приложении и почему не работает try/catch вокруг runApp()?

    Установите в main() два хука: FlutterError.onError для ошибок, которые фреймворк ловит в build, layout, paint и жестах, и PlatformDispatcher.instance.onError для необработанных асинхронных ошибок.

    void main() {  WidgetsFlutterBinding.ensureInitialized();   FlutterError.onError = (FlutterErrorDetails details) {    FlutterError.presentError(details);          // keep the console output    crashReporter.recordFlutterError(details);…
  19. 19

    Future в Dart нельзя отменить. Как остановить работу, когда пользователь уже ушёл с экрана?

    Отменяют то, что выполняет работу, а не Future: Future служит лишь дескриптором результата.

    class _SearchState extends State<Search> {  int _latest = 0;  List<Hit> _hits = const [];  CancelableOperation<List<Hit>>? _op;   Future<void> onQueryChanged(String q) async {…
  20. 20

    Вы сохраняете Future в поле и через секунду ожидаете его в try/catch, но приложение всё равно логирует необработанное исключение. Почему?

    Future сообщает об ошибке как о необработанной в момент завершения, если к нему ещё не подключён слушатель.

    class Warmup {  // wrong: starts now and may fail before anyone listens  final Future<Config> _config = loadConfig();   Future<void> run() async {    await Future<void>.delayed(const Duration(seconds: 1));…
  21. 21

    В крэш-репортах стек-трейс обрывается на event loop, а не на строке сбоя. Что теряет источник асинхронной ошибки?

    Любое место, где ошибку пробрасывают дальше или оборачивают без исходного StackTrace.

    // wrong: a new throw builds a new traceFuture<void> saveLosing(Order order) async {  try {    await repo.save(order);  } catch (e) {    throw SaveFailed('$e'); // the trace now starts on this line…
  22. 22

    Dart однопоточный. Как два асинхронных вызова всё равно могут устроить гонку и как сериализовать доступ к общему ресурсу?

    Каждый await создаёт паузу, в которую выполняется другой код, поэтому два вызова вида «прочитать, подождать, записать» могут перемешаться даже в одном потоке.

    class TokenManager {  Future<Tokens>? _refreshing;   // every caller during a refresh shares one request  Future<Tokens> refresh() =>      _refreshing ??= _doRefresh().whenComplete(() => _refreshing = null);…