UI को ब्लॉक किए या गलती से अपने अनुरोधों को दोबारा चलाए बिना Future, async/await और FutureBuilder में महारत हासिल करें।
इस पाठ को Kodokon में खोलेंDart आपके एप्लिकेशन को एक एकल थ्रेड पर चलाता है, जिसे एक इवेंट लूप संचालित करता है। कोई Future थ्रेड नहीं है: यह आने वाली किसी वैल्यू का वादा है। await उस थ्रेड को कभी ब्लॉक नहीं करता - यह मौजूदा फ़ंक्शन को निलंबित करता है, नियंत्रण लूप को वापस सौंप देता है (UI चलती रहती है), फिर परिणाम आने पर दोबारा शुरू होता है। परिणामस्वरूप: async/await शुद्ध गणना के लिए कुछ भी समानांतर नहीं करता; CPU-गहन काम के लिए, आपको एक आइसोलेट की ज़रूरत होती है।
Future<double> fetchRate() async {
await Future<void>.delayed(
const Duration(milliseconds: 300),
);
return 1.08;
}
Future<void> main() async {
try {
final rate = await fetchRate();
print('EUR/USD rate: $rate');
} on Exception catch (error) {
print('Rate loading failed: $error');
}
}दो लगातार await क्रमिक रूप से चलते हैं: दूसरा पहले के बाद ही शुरू होता है। जब क्रियाएँ स्वतंत्र हों, तो उन्हें Future.wait के साथ एक साथ शुरू करें। जानने योग्य समझौता: Future.wait फ़ेल-फ़ास्ट है - पहली त्रुटि पूरी चीज़ को अस्वीकार कर देती है और अन्य future के परिणाम खो जाते हैं, भले ही वे सफल हों। यदि आपको आंशिक परिणाम चाहिए, तो हर future के भीतर त्रुटि को संभालें।
Future<String> fetchProfile() async => 'profile';
Future<String> fetchOrders() async => 'orders';
Future<void> main() async {
final [profile, orders] = await Future.wait(
[fetchProfile(), fetchOrders()],
);
print('$profile / $orders');
}UI की ओर, FutureBuilder किसी Future को एक snapshot के ज़रिए build से जोड़ता है। व्यवहार में सबसे बड़ी गलती: future को सीधे build में बनाना। हर रीबिल्ड - किसी पैरेंट का एक साधारण setState ही काफ़ी है - तब अनुरोध को दोबारा चला देगा। future को initState में संग्रहीत करें और हमेशा डेटा से पहले त्रुटि की जाँच करें।
import 'package:flutter/material.dart';
class ProfileScreen extends StatefulWidget {
const ProfileScreen({super.key});
@override
State<ProfileScreen> createState() =>
_ProfileScreenState();
}
class _ProfileScreenState extends State<ProfileScreen> {
late final Future<String> _nameFuture;
@override
void initState() {
super.initState();
_nameFuture = _loadName();
}
Future<String> _loadName() async {
await Future<void>.delayed(
const Duration(seconds: 1),
);
return 'Ada Lovelace';
}
@override
Widget build(BuildContext context) {
return FutureBuilder<String>(
future: _nameFuture,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
}
if (!snapshot.hasData) {
return const Center(
child: CircularProgressIndicator(),
);
}
return Text(snapshot.data!);
},
);
}
}await वास्तव में मुख्य थ्रेड पर क्या करता है?Future हल नहीं हो जाता।Future.wait([a(), b()]) के साथ, यदि a() विफल हो जाए तो क्या होता है?Future.wait, a() की त्रुटि के साथ अस्वीकार कर देता है; b() का परिणाम खो जाता है।Future.wait, b() के समाप्त होने का इंतज़ार करता है, फिर अकेले उसका परिणाम लौटाता है।b() सफल होता है, त्रुटि को अनदेखा कर दिया जाता है।FutureBuilder के साथ build में सीधे लिखे गए future: fetchUser() से क्यों बचें?build में कोई एसिंक्रोनस कॉल नहीं हो सकती, कोड कंपाइल नहीं होता।Future बनाएगा, और इसलिए नेटवर्क अनुरोध को दोबारा चला देगा।snapshot एक प्रतीक्षा अवस्था में अटका रहेगा।