ورود و عضویت
0
سبد خرید شما خالی است
0
سبد خرید شما خالی است

آموزش برنامه نویسی غیر همزمان در فلاتر مفاهیم Async و Future

عنوان را به انگلیسی وارد کنید
4 دیدگاه
موضوع این نوشته را وارد کنید
زمان مطالعه را وارد کنید دقیقه برای مطالعه

در مدل معمولی برنامه نویسی زمانی که ما یک دستور را اجرا میکنیم کامپایلر تا مدت زمان تمام شدن این دستور هیچ کار دیگری انجام نمیدهد.

خب در این زمان اگر دستور ما زمانبر باشه برنامه به اصطلاح فریز میشه و کاربر تا پایان تموم شدن این دستور هیچ کار دیگری نمیتواند انجام دهد.

برای حل این مشکل اکثر زبان های برنامه از برنامه نویسی چند نخی یا مالتی تردینگ استفاده می کنند.

در زبان دارت ما بحث مربوط به مالتی تردینگ را نداریم زیرا این زبان فقط از یک Thread برای اجرا برنامه استفاده می کند.

البته برای شبیه سازی ترید ها مفهومی به نام Isolate در فلاتر را داریم.

مفهوم Async در فلاتر

تصور کنید در حال نوشتن برنامه‌ای هستید که باید اطلاعاتی را از یک منبع خارجی، مانند یک وب‌سایت یا پایگاه داده، دریافت کند.

در روش سنتی برنامه‌نویسی، این کار می‌تواند باعث کندی و عدم پاسخگویی برنامه شود، زیرا باید منتظر بمانید تا اطلاعات به طور کامل دریافت و پردازش شوند.

async و Future به عنوان راه حلی برای این مشکل پا به میدان می‌گذارند. با استفاده از این ابزارها، می‌توانید برنامه‌نویسی ناهمزمان را پیاده‌سازی کنید، به این معنی که برنامه شما می‌تواند بدون نیاز به انتظار برای دریافت اطلاعات، به کار خود ادامه دهد.

اما برای حل این مشکل از برنامه نویسی غیر همزمان با استفاده از دستورات کلیدی await, Future میتونیم استفاده کنیم.

آبجکت های Future در واقع نتیجه دستورات اجرا شده هستند که به برنامه برگشت داده می شوند.

برای مثال دانلود فایل کاری زمانبر هست و ما نمیخوایم در این مدت برنامه فریز بشه.

به همین دلیل از Future و await استفاده می کنیم.

زمانی که دانلود فایل به اتمام رسید با استفاده از آبجکت Future میتونیم از این عمل اطلاع پیدا کنیم.

همچنین با استفاده از await به نتیجه Future میتونیم دسترسی داشته باشیم. در بعضی مواقع نیاز هست که یک مقداری برگشت داده شود.

مزایای استفاده از async و Future:

  • بهبود تجربه کاربری: برنامه شما روان‌تر و پاسخگوتر خواهد بود، زیرا منتظر دریافت اطلاعات از منابع خارجی نمی‌ماند.
  • بهبود کارایی: می‌توانید از منابع سیستم خود به طور موثرتری استفاده کنید، زیرا برنامه شما می‌تواند چندین کار را به طور همزمان انجام دهد.
  • کاهش پیچیدگی کد: نوشتن کد ناهمزمان با استفاده از async و Future ساده‌تر و خواناتر است.

نحوه عملکرد async و Future:

  • async: کلمه کلیدی async برای تعریف یک تابع ناهمزمان استفاده می‌شود. این تابع می‌تواند بدون نیاز به انتظار برای دریافت اطلاعات، به کار خود ادامه دهد.
  • Future: Future یک شیء است که نشان‌دهنده یک نتیجه احتمالی در آینده است. این شیء می‌تواند شامل اطلاعاتی مانند وضعیت (موفقیت یا شکست) و داده‌های مربوط به نتیجه باشد.

پیاده سازی Future در فلاتر

آبجکت های Future کانستراکتور های متنوعی دارند.

کانستراکتور پیشفرض و اولیه Future یک متد async به عنوان پارامتر قبول می کند, زمانی که این متد یک مقدار برگرداند Future عملیات خودش را کامل کرده هست.

Future(() async {…});

اگر بخواهید که وقفه قبل از اجرا متد ایجاد کنید هم میتونید از دستور زیر استفاده کنید.

delayed(Duration)

همچنین اگر نیاز داشته باشید که چندین درخواست همزمان ارسال کنید میتونید از کانستراکتور زیر استفاده کنید تا از نتیجه هر درخواست اطلاع پیدا کنید.

wait(Iterable<Future>)

اگر متد شما هیچ مقداری برنمیگرداند باید متد خودتون و به شکل زیر از نوع void ایجاد کنید.

Future myFunc() async => …

مثال زیر روش های مختلف استفاده از Future و به خوبی نشون میده.

void main() async {
  /// Create a Future from the default constructor
  await Future(() async {
    /// Use the delayed constructor to create a Future that will resolve after x time.
    await Future<void>.delayed(Duration(seconds: 2));
    
    print("hello");
  });
  
  /// Do not freeze the code
  doARequest();
  
  /// Freeze the code
  await doARequest();
  
  /// It will not freeze the code because the function does not return an object of type Future
  await doARequest2();
  
  
  /// Freeze the code until the future is completed
  final String t = await retrieveStringFromStorage();
  final String t2 = await retrieveStringFromStorage2();
  
  /// Will print true !
  print(t == t2);
}

Future<void> doARequest() async {
  await requestToServer();
}

void doARequest2() async {
  await requestToServer();
}

Future<String> retrieveStringFromStorage() async {
  final String t = await getStringFromStorage();

  return t;
}

/// Is equivalent to
Future<String> retrieveStringFromStorage2() => getStringFromStorage();

بررسی کدهای مثال بالا:

1. تابع main:

  • خط 1: async، تابع main را به عنوان یک تابع ناهمزمان تعریف می‌کند. این کار به main اجازه می‌دهد تا از await استفاده کند و یک Future را برگرداند.
  • خطوط 3 تا 8:
    • یک Future تو در تو با استفاده از Future(() async {...}) ایجاد می‌شود.
    • با استفاده از Future<void>.delayed، اجرای کد به مدت 2 ثانیه به تعویق می‌افتد.
    • بعد از 2 ثانیه، عبارت “hello” چاپ می‌شود.
  • خط 10: تابع doARequest بدون استفاده از await فراخوانی می‌شود. به این ترتیب، این تابع به صورت موازی (غیرمسدود) اجرا می‌شود.
  • خط 12: await doARequest(). تا زمانی که Future تابع doARequest حل نشود، اجرای کد متوقف می‌شود.
  • خط 14: await doARequest2(). مشابه خط 12، اما doARequest2 یک Future را برنمی‌گرداند. به این ترتیب، این تابع مانند یک تابع معمولی عمل می‌کند و اجرای کد را مسدود نمی‌کند.
  • خطوط 16 و 17:
    • await retrieveStringFromStorage(). تا زمانی که Future رشته از حافظه ذخیره‌سازی بازیابی نشود، اجرای کد متوقف می‌شود.
    • await retrieveStringFromStorage2(). مشابه خط 16، اما از نحو کوتاه‌تر برای بازگرداندن Future به طور مستقیم استفاده می‌شود.
  • خط 19: مقایسه t و t2. با فرض اینکه هر دو رشته یکسان باشند، عبارت true چاپ می‌شود.

2. تابع doARequest:

  • خط 21: async. این تابع را به عنوان یک تابع ناهمزمان تعریف می‌کند.
  • خط 22: await requestToServer(). تا زمانی که Future تابع requestToServer حل نشود، اجرای کد متوقف می‌شود.

3. تابع doARequest2:

  • خط 25: async. این تابع را به عنوان یک تابع ناهمزمان تعریف می‌کند.
  • خط 26: await requestToServer(). مشابه خط 22.

4. تابع retrieveStringFromStorage:

  • خط 29: async. این تابع را به عنوان یک تابع ناهمزمان تعریف می‌کند.
  • خط 30: await getStringFromStorage(). تا زمانی که Future رشته از حافظه ذخیره‌سازی بازیابی نشود، اجرای کد متوقف می‌شود.
  • خط 31: رشته حل شده را برمی‌گرداند.

5. تابع retrieveStringFromStorage2:

  • خط 33: از نحو کوتاه‌تر برای بازگرداندن Future به طور مستقیم استفاده می‌شود. این تابع معادل retrieveStringFromStorage است.

نکات کلیدی:

  • async برنامه‌نویسی ناهمزمان را فعال می‌کند: توابع می‌توانند از await برای متوقف کردن اجرا تا زمانی که Futures حل شوند استفاده کنند.
  • await تا زمانی که یک Future کامل نشود، اجرا را متوقف می‌کند و مقدار آن را برمی‌گرداند یا خطایی را ایجاد می‌کند.
  • Future نشان‌دهنده عملیات ناهمزمان هستند که نتیجه آنها (حل یا رد شدن) در زمان فراخوانی مشخص نیست.
  • این کد هم ایجاد صریح (await) و هم ضمنی (Future<void>.delayedFutures را نشان می‌دهد.
  • تفاوت بین اجرای کد ناهمزمان و همزمان را درک کنید.

اگر میخواهید همزمان با تکمیل شدن درخواست دستورات شما ویو برنامه را آپدیت کنید میتونید از ویجت FutureBuilder کمک بگیرید. برای مثال زمانی که تا دریافت کامل اطلاعات نیاز دارید یک لودینگ به کاربر نمایش دهید.

کلاس Stream

استریم هم مثل Future در برنامه نویسی غیر همزمان نقش مهمی دارد.

قبلا بصورت مختصر در مورد Stream صحبت کردیم در مقاله زیر.

روش های مدیریت State در فلاتر

برای کار با استریم ابتدا باید یک آبجکت از کلاس StreamController ایجاد کنید.

این کلاس اجازه کنترل Stream و به شما میدهد. یعنی از طریق StreamController هست که میتونید رویداد های مختلف و در Stream ایجاد کنید.

اما این تمام کار نیست با استفاده از StreamController میتونید State ویجت استریم را چک کنید که در چه وضعیتی قرار دارد.

در فلاتر ما دو نوع استریم داریم که شامل Single-subscription stream و Broadcast stream می باشد.

Single-subscription stream

در این نوع استریم فقط اجازه استفاده به یک کلاینت داده می شود.

در این نوع استریم یک فضای بافر از ایونت ها ساخته می شود تا زمانی که یک کلاینت شروع به گوش دادن به این استریم کند.

و زمانی که کلاینت اتصال خود از استریم را قطع میکند دیگر به این ایونت ها دسترسی نخواهد داشت.

طراحی برنامه های واکنشگرا در فلاتر

Broadcast stream

این نوع استریم که امکان دسترسی چندین کلاینت به استریم را می دهد. اما هیچ نوع بافری در این نوع استریم وجود ندارد.

 import 'dart:async';

void main() {
  StreamLife streamLife = StreamLife();
}

class StreamLife {
  StreamLife() {
    controller2.add('eventBeforeListener');

    subscription1 = stream.listen((String value) {
      print(value + ' - sub1');
    });

    subscription2 = stream.listen((String value) {
      print(value + ' - sub2');
    });
    
    subscription3 = stream2.listen((String value) {
      print(value + ' - sub3');
    });
    
    launch();
  }

  final StreamController<String> controller = StreamController<String>.broadcast();
  final StreamController<String> controller2 = StreamController<String>();

  Stream<String> get stream => controller.stream;
  Stream<String> get stream2 => controller2.stream;
  
  StreamSubscription subscription1;
  StreamSubscription subscription2;
  StreamSubscription subscription3;
  
  Future<void> launch() async {
    await Future<void>.delayed(Duration(milliseconds: 100));
    
    controller.add('one');
   
    await Future<void>.delayed(Duration(milliseconds: 100));
    
    controller.add('two');
    
    await Future<void>.delayed(Duration(milliseconds: 100));
    
    subscription1.cancel();
    
    await Future<void>.delayed(Duration(milliseconds: 100));
    
    controller.add('tree');
    
    await Future<void>.delayed(Duration(milliseconds: 100));
    
    subscription2.cancel();

    await Future<void>.delayed(Duration(milliseconds: 100));
    
    controller2.add('ttt');

    await Future<void>.delayed(Duration(milliseconds: 100));
    
    try {
      stream2.listen((String v) {
        print(v);
      });
    }
    catch (e) {
      /// this will not work because there is already a listener on the stream2! (single subscription)
    }

    await Future<void>.delayed(Duration(milliseconds: 100));
    
    subscription3.cancel();
  }
}

خروجی کد بالا به شکل زیر می باشد.

 eventBeforeListtener — sub3
one — sub1 
one — sub2 
two — sub1 
two — sub2 
tree — sub2 
ttt — sub3

async و Future ابزارهای قدرتمندی برای برنامه‌نویسی ناهمزمان در فلاتر هستند. با استفاده از این ابزارها، می‌توانید برنامه‌های روان‌تر و پاسخگوتر با کارایی بهتر توسعه دهید.

4 پاسخ به “آموزش برنامه نویسی غیر همزمان در فلاتر مفاهیم Async و Future”

  1. Jafar گفت:

    بسیار عالی

  2. mohammad گفت:

    خیلی خوب و مفید بود

  3. golnar sheikh bahaie گفت:

    ممنونم

  4. asghar گفت:

    سلام
    چرا وقتی کد Stream شما رو کپی میکنم و اجرا میگیرم خطا میده.
    Unhandled exception:
    Class ‘_Type’ has no instance method ‘call’.

    NoSuchMethodError: method not found: ‘call’
    Receiver: Type: class ‘StreamLife’
    Arguments: []
    #0 Object._noSuchMethod (dart:core-patch/object_patch.dart:42)
    #1 Object.noSuchMethod (dart:core-patch/object_patch.dart:45)
    #2 main (file:///home/cg/root/4445237/main.dart:6:37)
    #3 _startIsolate. (dart:isolate-patch/isolate_patch.dart:255)
    #4 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:142)

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

Hesam
17 آگوست 2019
آموزش فارسی فلاتر
آموزش فارسی flutter