Какие способы создания Future в Dart ты знаешь?

Ответ

В Dart Future представляет отложенное вычисление или асинхронную операцию. Я создаю их несколькими способами в зависимости от задачи.

1. Использование конструкторов класса Future

Это низкоуровневые конструкторы для ручного создания Future.

  • Future(): Обертывает синхронную или асинхронную операцию, выполняемую в event loop.

    Future<int> heavyCalculation() {
      return Future(() {
        // Имитация долгой синхронной операции
        int result = 0;
        for (int i = 0; i < 1000000000; i++) {
          result += i;
        }
        return result; // Будет обернуто в завершенный Future
      });
    }
  • Future.delayed(): Создает Future, который завершается с результатом функции после задержки. Часто используется для имитации сетевых запросов или таймеров.

    Future<String> fetchUserData() {
      return Future.delayed(
        const Duration(seconds: 2),
        () => '{"name": "John"}', // Эта функция выполнится через 2 секунды
      );
    }
  • Future.value(): Немедленно создает завершенный успешный Future с заданным значением. Полезно в тестах или для возврата кэшированных данных.

    Future<Config> loadConfig() {
      if (_cachedConfig != null) {
        return Future.value(_cachedConfig!); // Немедленный возврат
      }
      return _fetchFromNetwork();
    }
  • Future.error(): Немедленно создает завершенный Future с ошибкой.

    Future<void> validateLogin(String email) {
      if (!email.contains('@')) {
        return Future.error(FormatException('Invalid email'));
      }
      return _checkServer();
    }

2. Использование async/await

Любая функция, помеченная ключевым словом async, автоматически возвращает Future. Это самый частый способ в production-коде.

Future<List<Post>> fetchPosts() async {
  // `await` приостанавливает выполнение функции, но не потока.
  final response = await http.get(Uri.parse('https://api.example.com/posts'));
  if (response.statusCode == 200) {
    return (jsonDecode(response.body) as List).map((e) => Post.fromJson(e)).toList();
  } else {
    throw Exception('Failed to load posts');
  }
}
// Функция fetchPosts возвращает Future<List<Post>>

3. Преобразование Stream в Future

Методы first, last, single, elementAt и reduce у Stream возвращают Future.

Future<int> getFirstValueFromStream(Stream<int> stream) {
  return stream.first; // Возвращает Future<int>
}

Выбор способа: Для работы с внешними ресурсами (сеть, БД) я использую async-функции. Для создания отложенных задач или моков в тестах — конструкторы Future.delayed или Future.value.

Ответ 18+ 🔞

А, вот это уже интересно! Слушай, тут про эти ваши Future в Dart. Ну, типа, обещание какое-то, которое выполнится потом, когда захочет. Как сосед, который должен сотку, но вернёт её, только когда у него премия на работе или тёща помрёт. В общем, создавать их можно по-разному, смотри.

1. Через конструкторы — это как вручную собирать мебель из IKEA

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

  • Просто Future(): Заворачиваешь какую-то долгую операцию, чтобы она не тормозила всё нахер. Типа, отдаёшь её в работу какому-то левому пулу, а сам идёшь пить кофе.

    Future<int> heavyCalculation() {
      return Future(() {
        // Представь, что тут какая-то пиздец какая тяжёлая математика
        int result = 0;
        for (int i = 0; i < 1000000000; i++) {
          result += i;
        }
        return result; // И вот этот результат засунут в Future и вернут тебе
      });
    }

    По сути говоришь: "Эй, выполни-ка это где-нибудь на фоне, а я потом заберу".

  • Future.delayed(): О, это классика! Создаёшь Future, который тупо будет спать заданное время, а потом что-то сделает. Идеально, чтобы сымитировать сетевой запрос, который всегда тормозит, как чёрт.

    Future<String> fetchUserData() {
      return Future.delayed(
        const Duration(seconds: 2), // Поспи, сука, две секунды
        () => '{"name": "John"}', // А потом проснись и верни эту хуйню
      );
    }

    Прям как мой интернет-провайдер — обещает высокую скорость, но только после ребута роутера через неопределённый промежуток времени.

  • Future.value(): А это для умников, которые всё уже посчитали. Future, который завершён сразу же, в момент создания. Как будто ты пообещал другу бутылку пива и тут же её ему сунул в руки. Никакой интриги!

    Future<Config> loadConfig() {
      if (_cachedConfig != null) {
        return Future.value(_cachedConfig!); // На, держи, не задерживай очередь!
      }
      return _fetchFromNetwork(); // А если нет в кэше, тогда иди, мучайся, как все.
    }
  • Future.error(): А это — сразу дать по морде ошибкой. Не стал даже пытаться что-то делать, сразу — на тебе, получай исключение, завернутое в Future. Жестоко, но иногда нужно.

    Future<void> validateLogin(String email) {
      if (!email.contains('@')) { // Если email кривой
        return Future.error(FormatException('Invalid email')); // Пиздуй отсюда с таким email!
      }
      return _checkServer(); // Ладно, если email норм, проверим на сервере.
    }

2. Магия async/await — как будто пишешь синхронный код

Вот это, блядь, красота! Ставишь async у функции — и она автоматом начинает возвращать Future. Внутри используешь await перед другой асинхронной хуйнёй, и код выглядит так, будто он выполняется построчно. Хотя на самом деле всё хитрее, епта.

Future<List<Post>> fetchPosts() async {
  // Тут `await` говорит: "Стой, блядь, на этом месте. Не иди дальше, пока не получишь ответ".
  // Но главный поток при этом не блокируется, он может чатики в телеге проверять.
  final response = await http.get(Uri.parse('https://api.example.com/posts'));
  if (response.statusCode == 200) {
    return (jsonDecode(response.body) as List).map((e) => Post.fromJson(e)).toList();
  } else {
    throw Exception('Failed to load posts'); // Если сервер лёг, кидаем ошибку
  }
}
// Сама функция fetchPosts всё равно вернёт тебе Future<List<Post>>, это железно.

Это как заказать еду с доставкой (async функция) и ждать курьера у двери (await), но при этом ты можешь параллельно мыть посуду (рисовать UI), а не стоять как идиот, уставившись в глазок.

3. Выковыривание Future из Stream

Stream — это как труба, из которой данные прут потоком. А иногда тебе надо вытащить оттуда всего один кусок и завернуть его в Future. На помощь приходят методы вроде first.

Future<int> getFirstValueFromStream(Stream<int> stream) {
  return stream.first; // Дай мне первый элемент из этого потока и заверни в Future, пожалуйста!
}

Сидишь, ждёшь, когда в трубе что-то появится, хватаешь первое, что вылезло, и довольный уходишь.

А теперь, блядь, главное: Если пишешь нормальный код и работаешь с сетью, базой или файлами — юзай async/await, не еби мозг. Если надо в тестах что-то замокать или сделать таймер — Future.delayed или Future.value тебе в помощь. А если чувствуешь себя хакером и хошь покопаться в кишках — тогда конструкторы. Но без фанатизма, а то сам потом не разберёшься.