Ответ
Future.wait() — это статический метод класса Future, который позволяет параллельно запустить несколько асинхронных операций и дождаться результатов выполнения всех из них. Он принимает Iterable<Future> и возвращает Future<List<T>>, где T — это тип результата каждого из переданных Future.
Базовый пример:
Future<String> fetchUserProfile() async {
await Future.delayed(Duration(seconds: 1)); // Имитация сетевого запроса
return 'John Doe';
}
Future<int> fetchUserScore() async {
await Future.delayed(Duration(seconds: 2));
return 95;
}
Future<void> main() async {
// Запускаем оба Future параллельно. Общее время ~2 сек, а не 3.
final List<dynamic> results = await Future.wait([
fetchUserProfile(),
fetchUserScore(),
]);
print('Profile: ${results[0]}, Score: ${results[1]}');
// Вывод через ~2 секунды: Profile: John Doe, Score: 95
}
Ключевое поведение и обработка ошибок:
- Параллельное выполнение: Все Future в списке начинают выполняться немедленно, а не последовательно.
- Завершение: Возвращаемый Future завершится только когда завершатся все переданные Future.
- Ошибки: Если любой из Future завершится с ошибкой, то весь
Future.waitнемедленно завершится с этой ошибкой, и результаты остальных Future будут потеряны.// ❌ Проблема: если fetchUserProfile упадёт, мы не получим даже успешный результат fetchUserScore. try { final results = await Future.wait([fetchUserProfile(), fetchUserScore()]); } catch (e) { print('Один из запросов упал: $e'); }
Практические сценарии использования во Flutter:
-
Параллельная загрузка данных для экрана:
Future<void> loadInitialData() async { // Параллельно загружаем пользователя, настройки и список друзей final results = await Future.wait([ _userRepository.getCurrentUser(), _settingsRepository.fetchSettings(), _friendsRepository.getFriendList(), ]); setState(() { _currentUser = results[0] as User; _settings = results[1] as AppSettings; _friends = results[2] as List<Friend>; }); } -
Надёжная обработка ошибок с
try-catchдля каждого Future:Future<void> loadDataSafely() async { // Создаём Future, которые никогда не падают, а возвращают Result объект final futures = [ _fetchData1().then((value) => Result.success(value)).catchError((e) => Result.failure(e)), _fetchData2().then((value) => Result.success(value)).catchError((e) => Result.failure(e)), ]; final List<Result> results = await Future.wait(futures); // Теперь можем обработать успешные и неуспешные результаты независимо for (final result in results) { if (result.isSuccess) { print('Данные: ${result.value}'); } else { print('Ошибка загрузки: ${result.error}'); } } } -
Отправка нескольких независимых аналитических событий:
Future<void> logAnalyticsEvents() async { // Нам не важен результат, и мы не хотим ждать. Используем wait в isolate. unawaited(Future.wait([ _analytics.logEvent('screen_opened'), _analytics.logEvent('user_action', params: {'action': 'click'}), ])); }
Альтернатива для независимой обработки ошибок: Если нужно, чтобы каждый Future завершился независимо, и вы могли обработать все результаты и ошибки, рассмотрите использование Future.forEach с индивидуальным try-catch внутри цикла или пакет async с функцией FutureGroup.
Ответ 18+ 🔞
А, вот эта штука, Future.wait()! Ну, смотри, представь себе такую картину. Ты стоишь на кухне, голодный как собака, и тебе надо и макароны сварить, и котлеты пожарить, и чайник вскипятить. Можно делать всё по очереди — это как await для каждой операции. Но это же, ёпта, овердохуища времени займёт! Ты будешь ждать, пока вода закипит, потом ставить макароны, потом сковородку гре... Короче, терпения ноль ебать.
А можно по-взрослому. Включил все конфорки сразу, на одну — чайник, на другую — кастрюлю с водой для макарон, на третью — сковородку. И всё это греется параллельно. Вот Future.wait() — это ты как раз шеф-повар на такой кухне. Ты говоришь: "Так, все мои асинхронные операции (Future), начинайте работать одновременно, а я подожду, пока все доедят".
Простой пример, чтобы въехать:
Future<String> скачатьАватарку() async {
await Future.delayed(Duration(seconds: 1)); // Представь, что это сетевой запрос
return 'база данных вернула ссылку на фото';
}
Future<int> получитьБаллы() async {
await Future.delayed(Duration(seconds: 2));
return 100500;
}
Future<void> main() async {
// Запускаем два запроса ПАРАЛЛЕЛЬНО. Вместо 3 секунд (1+2) ждём всего ~2.
final List<dynamic> результаты = await Future.wait([
скачатьАватарку(),
получитьБаллы(),
]);
print('Аватар: ${результаты[0]}, Баллы: ${результаты[1]}');
// Вывод через ~2 секунды: Аватар: база данных..., Баллы: 100500
}
Важные моменты, где можно обжечься:
- Параллельность: Да, всё стартует сразу. Это не цепочка
await. - Ожидание: Ты получишь результат, только когда последний из этих Future отчитается. Даже если первый управился за милисекунду.
- Главная засада — ошибки. Представь, ты послал трёх курьеров: одного за пиццей, второго за колой, третьего за салфетками. Используешь
Future.wait()за всеми. И тут второго курьера с колой сбивает маршрутка. Что делаетFuture.wait? Он такой: "Всё, доверия ебать ноль!" — и тебе прилетает одна большая ошибка "курьер №2 мертв". И ты даже не узнаешь, что пицца-то уже приехала и салфетки тоже! Результаты успешных Future просто накрываются медным тазом.
// ❌ Проблема: если скачатьАватарку() упадёт, баллы мы тоже не увидим.
try {
final results = await Future.wait([скачатьАватарку(), получитьБаллы()]);
} catch (e) {
print('Один из запросов сдох: $e'); // И всё, хуй что поймёшь, какой именно.
}
Где это реально полезно во Flutter:
-
Загрузка всей хуйни для экрана сразу:
Future<void> загрузитьЭкран() async { // Параллельно дербаним сервер на три части: юзер, посты, настройки. final results = await Future.wait([ _api.получитьЮзера(), _api.получитьЛенту(), _localStorage.загрузитьНастройки(), ]); setState(() { _юзер = results[0] as User; _посты = results[1] as List<Post>; _настройки = results[2] as Settings; }); // Экран построился из всего сразу, а не кусками. } -
Хитрая жопа: обработка ошибок для каждого отдельно. Чтобы не терять результаты из-за одного говнаря.
Future<void> загрузитьБезопасно() async { // Каждый Future оборачиваем так, чтобы он не падал, а возвращал "результат или ошибку" final futures = [ _запрос1().then((v) => Успех(v)).catchError((e) => Провал(e)), _запрос2().then((v) => Успех(v)).catchError((e) => Провал(e)), ]; final List<dynamic> результаты = await Future.wait(futures); // Тут уже ничего не упадёт for (final res in результаты) { if (res is Успех) { print('Опа, данные: ${res.значение}'); } else if (res is Провал) { print('Ну вот, опять: ${res.ошибка}'); } } } -
Отправка аналитики, когда похуй на результат. Кинул кучу событий в параллель и забыл.
Future<void> отправитьСобытия() async { // Нам не нужно ждать ответа от сервера аналитики. unawaited чтобы не ругался линтер. unawaited(Future.wait([ _analytics.событие('экран_открыт'), _analytics.событие('кнопка_тыкнута', параметры: {'id': 123}), ])); // Отправили и пошли дальше, не ждём. }
Если тебе прям вот надо, чтобы каждый Future жил своей жизнью и ошибки одного не убивали других — тогда Future.wait в лоб не катит. Смотри в сторону Future.forEach с индивидуальными try-catch внутри или ищи пакеты вроде async, там есть FutureGroup. Но для большинства случаев, когда тебе нужны все результаты разом, Future.wait() — это, бля, пизда рулю. Главное — помни про ошибки, а то сам от себя охуеешь, когда всё падает из-за одной ерунды.