Kodokon kodokon.com

Dart में एसिंक्रोनस: Future, async/await, FutureBuilder

UI को ब्लॉक किए या गलती से अपने अनुरोधों को दोबारा चलाए बिना Future, async/await और FutureBuilder में महारत हासिल करें।

10 मिनट · 3 प्रश्न

इस पाठ को Kodokon में खोलें

Dart आपके एप्लिकेशन को एक एकल थ्रेड पर चलाता है, जिसे एक इवेंट लूप संचालित करता है। कोई Future थ्रेड नहीं है: यह आने वाली किसी वैल्यू का वादा है। await उस थ्रेड को कभी ब्लॉक नहीं करता - यह मौजूदा फ़ंक्शन को निलंबित करता है, नियंत्रण लूप को वापस सौंप देता है (UI चलती रहती है), फिर परिणाम आने पर दोबारा शुरू होता है। परिणामस्वरूप: async/await शुद्ध गणना के लिए कुछ भी समानांतर नहीं करता; CPU-गहन काम के लिए, आपको एक आइसोलेट की ज़रूरत होती है।

DART
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');
  }
}
किसी async फ़ंक्शन का उपभोग await और try/catch के साथ किया जाता है।

दो लगातार await क्रमिक रूप से चलते हैं: दूसरा पहले के बाद ही शुरू होता है। जब क्रियाएँ स्वतंत्र हों, तो उन्हें Future.wait के साथ एक साथ शुरू करें। जानने योग्य समझौता: Future.wait फ़ेल-फ़ास्ट है - पहली त्रुटि पूरी चीज़ को अस्वीकार कर देती है और अन्य future के परिणाम खो जाते हैं, भले ही वे सफल हों। यदि आपको आंशिक परिणाम चाहिए, तो हर future के भीतर त्रुटि को संभालें।

DART
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');
}
Future.wait और Dart 3 डीस्ट्रक्चरिंग के साथ समानांतरीकरण।

UI की ओर, FutureBuilder किसी Future को एक snapshot के ज़रिए build से जोड़ता है। व्यवहार में सबसे बड़ी गलती: future को सीधे build में बनाना। हर रीबिल्ड - किसी पैरेंट का एक साधारण setState ही काफ़ी है - तब अनुरोध को दोबारा चला देगा। future को initState में संग्रहीत करें और हमेशा डेटा से पहले त्रुटि की जाँच करें।

DART
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!);
      },
    );
  }
}
future केवल एक बार, initState में बनाया जाता है।

ज्ञान जांच

सुनिश्चित करें कि आपको इस पाठ के मुख्य बिंदु याद हैं।

  1. await वास्तव में मुख्य थ्रेड पर क्या करता है?
    • यह थ्रेड को तब तक ब्लॉक करता है जब तक Future हल नहीं हो जाता।
    • यह गणना को स्वचालित रूप से किसी द्वितीयक थ्रेड पर स्थानांतरित कर देता है।
    • यह मौजूदा फ़ंक्शन को निलंबित करता है और नियंत्रण इवेंट लूप को वापस सौंप देता है: UI चलती रहती है।
  2. Future.wait([a(), b()]) के साथ, यदि a() विफल हो जाए तो क्या होता है?
    • Future.wait, a() की त्रुटि के साथ अस्वीकार कर देता है; b() का परिणाम खो जाता है।
    • Future.wait, b() के समाप्त होने का इंतज़ार करता है, फिर अकेले उसका परिणाम लौटाता है।
    • जब तक b() सफल होता है, त्रुटि को अनदेखा कर दिया जाता है।
  3. FutureBuilder के साथ build में सीधे लिखे गए future: fetchUser() से क्यों बचें?
    • build में कोई एसिंक्रोनस कॉल नहीं हो सकती, कोड कंपाइल नहीं होता।
    • हर रीबिल्ड एक नया Future बनाएगा, और इसलिए नेटवर्क अनुरोध को दोबारा चला देगा।
    • snapshot एक प्रतीक्षा अवस्था में अटका रहेगा।