در مدل معمولی برنامه نویسی زمانی که ما یک دستور را اجرا میکنیم کامپایلر تا مدت زمان تمام شدن این دستور هیچ کار دیگری انجام نمیدهد.
خب در این زمان اگر دستور ما زمانبر باشه برنامه به اصطلاح فریز میشه و کاربر تا پایان تموم شدن این دستور هیچ کار دیگری نمیتواند انجام دهد.
برای حل این مشکل اکثر زبان های برنامه از برنامه نویسی چند نخی یا مالتی تردینگ استفاده می کنند.
در زبان دارت ما بحث مربوط به مالتی تردینگ را نداریم زیرا این زبان فقط از یک Thread برای اجرا برنامه استفاده می کند.
البته برای شبیه سازی ترید ها مفهومی به نام Isolate در فلاتر را داریم.
تصور کنید در حال نوشتن برنامهای هستید که باید اطلاعاتی را از یک منبع خارجی، مانند یک وبسایت یا پایگاه داده، دریافت کند.
در روش سنتی برنامهنویسی، این کار میتواند باعث کندی و عدم پاسخگویی برنامه شود، زیرا باید منتظر بمانید تا اطلاعات به طور کامل دریافت و پردازش شوند.
async و Future به عنوان راه حلی برای این مشکل پا به میدان میگذارند. با استفاده از این ابزارها، میتوانید برنامهنویسی ناهمزمان را پیادهسازی کنید، به این معنی که برنامه شما میتواند بدون نیاز به انتظار برای دریافت اطلاعات، به کار خود ادامه دهد.
اما برای حل این مشکل از برنامه نویسی غیر همزمان با استفاده از دستورات کلیدی await, Future میتونیم استفاده کنیم.
آبجکت های Future در واقع نتیجه دستورات اجرا شده هستند که به برنامه برگشت داده می شوند.
برای مثال دانلود فایل کاری زمانبر هست و ما نمیخوایم در این مدت برنامه فریز بشه.
به همین دلیل از Future و await استفاده می کنیم.
زمانی که دانلود فایل به اتمام رسید با استفاده از آبجکت Future میتونیم از این عمل اطلاع پیدا کنیم.
همچنین با استفاده از await به نتیجه Future میتونیم دسترسی داشته باشیم. در بعضی مواقع نیاز هست که یک مقداری برگشت داده شود.
نحوه عملکرد async و Future:
async
برای تعریف یک تابع ناهمزمان استفاده میشود. این تابع میتواند بدون نیاز به انتظار برای دریافت اطلاعات، به کار خود ادامه دهد.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
:
async
، تابع main
را به عنوان یک تابع ناهمزمان تعریف میکند. این کار به main
اجازه میدهد تا از await
استفاده کند و یک Future
را برگرداند.Future
تو در تو با استفاده از Future(() async {...})
ایجاد میشود.Future<void>.delayed
، اجرای کد به مدت 2 ثانیه به تعویق میافتد.doARequest
بدون استفاده از await
فراخوانی میشود. به این ترتیب، این تابع به صورت موازی (غیرمسدود) اجرا میشود.await doARequest()
. تا زمانی که Future
تابع doARequest
حل نشود، اجرای کد متوقف میشود.await doARequest2()
. مشابه خط 12، اما doARequest2
یک Future
را برنمیگرداند. به این ترتیب، این تابع مانند یک تابع معمولی عمل میکند و اجرای کد را مسدود نمیکند.await retrieveStringFromStorage()
. تا زمانی که Future
رشته از حافظه ذخیرهسازی بازیابی نشود، اجرای کد متوقف میشود.await retrieveStringFromStorage2()
. مشابه خط 16، اما از نحو کوتاهتر برای بازگرداندن Future
به طور مستقیم استفاده میشود.t
و t2
. با فرض اینکه هر دو رشته یکسان باشند، عبارت true
چاپ میشود.2. تابع doARequest
:
async
. این تابع را به عنوان یک تابع ناهمزمان تعریف میکند.await requestToServer()
. تا زمانی که Future
تابع requestToServer
حل نشود، اجرای کد متوقف میشود.3. تابع doARequest2
:
async
. این تابع را به عنوان یک تابع ناهمزمان تعریف میکند.await requestToServer()
. مشابه خط 22.4. تابع retrieveStringFromStorage
:
async
. این تابع را به عنوان یک تابع ناهمزمان تعریف میکند.await getStringFromStorage()
. تا زمانی که Future
رشته از حافظه ذخیرهسازی بازیابی نشود، اجرای کد متوقف میشود.5. تابع retrieveStringFromStorage2
:
Future
به طور مستقیم استفاده میشود. این تابع معادل retrieveStringFromStorage
است.نکات کلیدی:
async
برنامهنویسی ناهمزمان را فعال میکند: توابع میتوانند از await
برای متوقف کردن اجرا تا زمانی که Future
s حل شوند استفاده کنند.await
تا زمانی که یک Future
کامل نشود، اجرا را متوقف میکند و مقدار آن را برمیگرداند یا خطایی را ایجاد میکند.Future
نشاندهنده عملیات ناهمزمان هستند که نتیجه آنها (حل یا رد شدن) در زمان فراخوانی مشخص نیست.await
) و هم ضمنی (Future<void>.delayed
) Future
s را نشان میدهد.اگر میخواهید همزمان با تکمیل شدن درخواست دستورات شما ویو برنامه را آپدیت کنید میتونید از ویجت FutureBuilder کمک بگیرید. برای مثال زمانی که تا دریافت کامل اطلاعات نیاز دارید یک لودینگ به کاربر نمایش دهید.
استریم هم مثل Future در برنامه نویسی غیر همزمان نقش مهمی دارد.
قبلا بصورت مختصر در مورد Stream صحبت کردیم در مقاله زیر.
برای کار با استریم ابتدا باید یک آبجکت از کلاس StreamController ایجاد کنید.
این کلاس اجازه کنترل Stream و به شما میدهد. یعنی از طریق StreamController هست که میتونید رویداد های مختلف و در Stream ایجاد کنید.
اما این تمام کار نیست با استفاده از StreamController میتونید State ویجت استریم را چک کنید که در چه وضعیتی قرار دارد.
در فلاتر ما دو نوع استریم داریم که شامل 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 ابزارهای قدرتمندی برای برنامهنویسی ناهمزمان در فلاتر هستند. با استفاده از این ابزارها، میتوانید برنامههای روانتر و پاسخگوتر با کارایی بهتر توسعه دهید.
بسیار عالی
خیلی خوب و مفید بود
ممنونم
سلام
چرا وقتی کد 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)