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

Для чего нужны Future?

1.0 Junior🔥 231 комментариев
#Dart#Асинхронность

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

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

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

Для чего нужны Future?

Future — это объект, который представляет значение, которое может быть доступно сейчас или в будущем. Future используются для работы с асинхронными операциями, такими как сетевые запросы, загрузка файлов, доступ к базе данных. Это критично для создания отзывчивых приложений, которые не блокируют UI.

Проблема, которую решают Future

Синхронный (блокирующий) код:

// ❌ Плохо - приложение зависает!
void loadUser() {
  final response = http.get('https://api.example.com/user'); // Ждет 2 секунды
  print(response.body); // Только потом продолжает
  // UI заморозилось на 2 секунды!
}

Асинхронный код с Future:

// ✅ Хорошо - UI остается отзывчивым!
Future<String> loadUser() async {
  final response = await http.get('https://api.example.com/user');
  return response.body; // Продолжить когда данные пришли
  // UI не блокируется!
}

Что такое Future?

Future — это объект-обертка для асинхронного результата:

// Future может быть в одном из трех состояний:

Future<String> userFuture = fetchUser();

// 1. Pending (ожидание)
print(userFuture); // Instance of 'Future<String>'

// 2. Resolved (успешно выполнено)
Future<String> resolvedFuture = Future.value('John');
await resolvedFuture; // 'John'

// 3. Rejected (ошибка)
Future<String> rejectedFuture = Future.error('Network error');
// Выбросит исключение при await

Три состояния Future

Применимо на временной шкале:

0ms          1000ms              2000ms
├─ Pending   ├─ выполняется      ├─ Resolved или Rejected
│            │                   │
│            │                   └─> Завершено с 'John'
│            │                   └─> Ошибка: 'Network error'
└────────────┴───────────────────┘

Code:
Future<String> user = fetchUser(); // Pending
await user;                         // Resolved/Rejected

Базовый пример

// Создание Future
Future<String> fetchUser() async {
  await Future.delayed(Duration(seconds: 2));
  return 'John Doe';
}

// Использование Future
void main() async {
  print('Start'); // Выведется сразу
  
  final user = await fetchUser();
  print('User: $user'); // Выведется через 2 секунды
  
  print('End'); // Выведется в конце
}

// Вывод:
// Start
// User: John Doe (через 2 сек)
// End

Работа с Future

1. await (ожидание)

Future<String> getName() async {
  return 'John';
}

void main() async {
  final name = await getName();
  print('Name: $name'); // John
}

2. then() (цепочка)

fetchUser()
  .then((user) {
    print('User: $user');
    return user.email;
  })
  .then((email) {
    print('Email: $email');
  })
  .catchError((error) {
    print('Error: $error');
  });

3. catchError() (обработка ошибок)

try {
  final result = await riskyOperation();
  print('Success: $result');
} catch (e) {
  print('Error: $e');
}

// Или
riskyOperation()
  .then((result) => print('Success: $result'))
  .catchError((error) => print('Error: $error'));

Future в Flutter UI

FutureBuilder — встроенный виджет для работы с Future:

class UserProfile extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return FutureBuilder<User>(
      future: fetchUser(), // Future<User>
      builder: (context, snapshot) {
        // Три состояния:
        if (snapshot.connectionState == ConnectionState.waiting) {
          return CircularProgressIndicator(); // Загрузка
        }
        
        if (snapshot.hasError) {
          return Text('Error: ${snapshot.error}'); // Ошибка
        }
        
        if (snapshot.hasData) {
          final user = snapshot.data!;
          return Column(
            children: [
              Text('Name: ${user.name}'),
              Text('Email: ${user.email}'),
            ],
          ); // Успех
        }
        
        return SizedBox(); // Неожиданное состояние
      },
    );
  }
}

Практические примеры

Пример 1: HTTP запрос

import 'package:http/http.dart' as http;
import 'dart:convert';

class User {
  final String id;
  final String name;
  final String email;
  
  User({
    required this.id,
    required this.name,
    required this.email,
  });
  
  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      id: json['id'],
      name: json['name'],
      email: json['email'],
    );
  }
}

// Future для HTTP запроса
Future<User> fetchUser(String userId) async {
  try {
    final response = await http.get(
      Uri.parse('https://api.example.com/users/$userId'),
      headers: {'Accept': 'application/json'},
    ).timeout(Duration(seconds: 10)); // Timeout
    
    if (response.statusCode == 200) {
      return User.fromJson(jsonDecode(response.body));
    } else {
      throw Exception('Failed to load user');
    }
  } on TimeoutException {
    throw Exception('Request timeout');
  } on SocketException {
    throw Exception('No internet connection');
  }
}

// Использование
class UserScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return FutureBuilder<User>(
      future: fetchUser('123'),
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          return Scaffold(
            appBar: AppBar(title: Text('Loading...')),
            body: Center(child: CircularProgressIndicator()),
          );
        }
        
        if (snapshot.hasError) {
          return Scaffold(
            appBar: AppBar(title: Text('Error')),
            body: Center(child: Text('${snapshot.error}')),
          );
        }
        
        final user = snapshot.data!;
        return Scaffold(
          appBar: AppBar(title: Text('User Profile')),
          body: Center(
            child: Column(
              children: [
                Text('ID: ${user.id}'),
                Text('Name: ${user.name}'),
                Text('Email: ${user.email}'),
              ],
            ),
          ),
        );
      },
    );
  }
}

Пример 2: Чтение файла

import 'dart:io';

Future<String> readFile(String filePath) async {
  try {
    final file = File(filePath);
    return await file.readAsString();
  } catch (e) {
    throw Exception('Failed to read file: $e');
  }
}

// Использование
void main() async {
  try {
    final content = await readFile('data.txt');
    print('Content: $content');
  } catch (e) {
    print('Error: $e');
  }
}

Пример 3: Future.delayed (имитация задержки)

// Для тестирования
Future<String> simulateNetworkCall() async {
  await Future.delayed(Duration(seconds: 2));
  return 'Data loaded';
}

// Использование
class LoadingWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return FutureBuilder<String>(
      future: simulateNetworkCall(),
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          return Center(child: CircularProgressIndicator());
        }
        return Center(child: Text(snapshot.data!));
      },
    );
  }
}

Работа с несколькими Future

Future.wait() — ждем все Future

Future<void> loadMultipleData() async {
  try {
    final results = await Future.wait([
      fetchUser('1'),
      fetchPosts('1'),
      fetchComments('1'),
    ]);
    
    print('All loaded: $results');
  } catch (e) {
    print('Error: $e');
  }
}

Future.any() — первый успешный Future

Future<String> fetchFromAnyServer() async {
  return Future.any([
    http.get('https://server1.com').timeout(Duration(seconds: 5)),
    http.get('https://server2.com').timeout(Duration(seconds: 5)),
    http.get('https://server3.com').timeout(Duration(seconds: 5)),
  ]);
}

Сравнение: Future vs Stream

Future                  | Stream
────────────────────────┼──────────────────────
Одно значение           | Множество значений
Время выполнения        | Несколько значений во времени
Примеры:                | Примеры:
- HTTP запрос           | - WebSocket
- Загрузка файла        | - Sensor events
- Чтение из БД          | - User input stream
- Простые async ops     | - Real-time данные

Лучшие практики

1. Всегда обрабатывай ошибки

// ✅ Хорошо
try {
  final result = await riskyOperation();
} catch (e) {
  print('Error: $e');
}

// ❌ Плохо - может выбросить необработанное исключение
final result = await riskyOperation();

2. Используй FutureBuilder в UI

// ✅ Хорошо
FutureBuilder<Data>(
  future: fetchData(),
  builder: (context, snapshot) {
    // Обработка всех состояний
  },
)

// ❌ Плохо - может зависнуть
await fetchData(); // В build методе!

3. Используй timeout для сетевых запросов

// ✅ Хорошо
final response = await http.get(url).timeout(Duration(seconds: 10));

// ❌ Плохо - может висеть вечно
final response = await http.get(url);

Отличие async/await от then()

// Способ 1: async/await (рекомендуется)
Future<String> loadData() async {
  try {
    final result = await fetchData();
    return 'Loaded: $result';
  } catch (e) {
    return 'Error: $e';
  }
}

// Способ 2: then() (старый стиль)
Future<String> loadData() {
  return fetchData()
    .then((result) => 'Loaded: $result')
    .catchError((e) => 'Error: $e');
}

// async/await более читаемый и приобретен!

Вывод

Future — это фундаментальный концепт асинхронного программирования в Dart:

Не блокирует UI — приложение остается отзывчивым ✅ Простой синтаксис — async/await ✅ Обработка ошибок — try/catch ✅ Встроена в Flutter — FutureBuilder ✅ Основа для сетевых запросов — HTTP, WebSocket

Правило большого пальца:

  • Долгоживущая операция → Future
  • Несколько значений во времени → Stream
  • В UI → FutureBuilder
  • Несколько Future → Future.wait()

Без Future мобильные приложения были бы медленными и завислими. Future — это то, что делает Flutter приложения гладкими и отзывчивыми!

Для чего нужны Future? | PrepBro