Future และ Async/Await
Future<T> ใน Dart คือ Promise<T> ใน JavaScript ทั้งคู่แทนค่า single value ที่ยังไม่พร้อมใช้งาน — จะมาถึง (หรือล้มเหลว) ในบางจุดในอนาคต แนวคิดทุกอย่างที่คุณรู้จาก JS Promise แมปได้โดยตรง: .then(), .catch(), async/await, try/catch และการประมวลผลแบบ parallel ด้วย Future.wait / Promise.all mental model เหมือนกัน เพียงแต่ API surface เปลี่ยนไปเล็กน้อย
เปรียบเทียบแบบ side-by-side
หัวข้อที่มีชื่อว่า “เปรียบเทียบแบบ side-by-side”// --- Promise chain style ---function loadUser(id) { return fetch(`/api/users/${id}`) .then(res => res.json()) .then(data => { console.log('User:', data.name); return data; }) .catch(err => { console.error('Failed:', err.message); });}
// --- async/await style ---async function loadUserAsync(id) { try { const res = await fetch(`/api/users/${id}`); const data = await res.json(); console.log('User:', data.name); return data; } catch (err) { console.error('Failed:', err.message); }}// --- Future chain style ---Future<void> loadUser(String id) { return Future.delayed( const Duration(seconds: 1), () => {'name': 'Ava', 'id': id}, ) .then((data) { print('User: ${data['name']}'); }) .catchError((err) { print('Failed: $err'); });}
// --- async/await style ---Future<void> loadUserAsync(String id) async { try { // Simulating an HTTP call with Future.delayed final data = await Future.delayed( const Duration(seconds: 1), () => {'name': 'Ava', 'id': id}, ); print('User: ${data['name']}'); } catch (err) { print('Failed: $err'); }}ตัวอย่างที่รันได้จริง
หัวข้อที่มีชื่อว่า “ตัวอย่างที่รันได้จริง”แอปด้านล่างแสดงการทำงานของ FutureBuilder ที่ consume Future.delayed — รูปแบบ Flutter ที่เทียบเท่ากับ useEffect(() => { fetch(...).then(setData) }, []) สังเกต spinner หายไปและผลลัพธ์ปรากฏหลังจากสองวินาที
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(
body: Center(child: FutureDemo()),
),
);
}
}
// Simulates a network call — replace with http.get() in a real app.
Future<String> fetchMessage() {
return Future.delayed(
const Duration(seconds: 2),
() => 'Hello from the future!',
);
}
class FutureDemo extends StatefulWidget {
const FutureDemo({super.key});
@override
State<FutureDemo> createState() => _FutureDemoState();
}
class _FutureDemoState extends State<FutureDemo> {
// Hold the Future in state so it does not restart on every rebuild.
late final Future<String> _messageFuture;
@override
void initState() {
super.initState();
_messageFuture = fetchMessage();
}
@override
Widget build(BuildContext context) {
return FutureBuilder<String>(
future: _messageFuture,
builder: (context, snapshot) {
// ConnectionState.waiting === Promise pending
if (snapshot.connectionState == ConnectionState.waiting) {
return const Column(
mainAxisSize: MainAxisSize.min,
children: [
CircularProgressIndicator(),
SizedBox(height: 16),
Text(
'Awaiting the future...',
style: TextStyle(color: Colors.grey, fontSize: 14),
),
],
);
}
// snapshot.hasError === Promise rejected
if (snapshot.hasError) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.error_outline, color: Colors.red, size: 40),
const SizedBox(height: 12),
Text(
'Error: ${snapshot.error}',
style: const TextStyle(color: Colors.red, fontSize: 14),
),
],
);
}
// snapshot.data present === Promise resolved
return Column(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.check_circle_outline,
color: Colors.green, size: 48),
const SizedBox(height: 12),
Text(
snapshot.data ?? '',
style: const TextStyle(
fontSize: 22, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
const Text(
'Future resolved — like a Promise.then() callback.',
style: TextStyle(color: Colors.grey, fontSize: 13),
textAlign: TextAlign.center,
),
],
);
},
);
}
}