آموزش کامل async و await با مثالهای C# و JavaScript
آموزش کامل async و await با مثالهای C# و JavaScript
مقدمه
برنامهنویسی همزمان (Asynchronous Programming) یکی از مباحث مهم دنیای امروز توسعه نرمافزار است. بسیاری از عملیاتها مثل درخواست به سرور، خواندن فایل یا کارهای زمانبر دیگر، اگر بهصورت همزمان اجرا نشوند باعث کند شدن برنامه و قفل شدن رابط کاربری میشوند.
دو کلیدواژهی اصلی برای مدیریت این عملیاتها عبارتند از:
async
: مشخص میکند که یک متد یا تابع، غیرهمزمان است.await
: مشخص میکند که اجرای کد باید منتظر تکمیل یک عملیات غیرهمزمان بماند.
بخش اول: async و await در C#
تعریف ساده
در سیشارپ، وقتی متدی را با async
تعریف میکنیم، آن متد معمولاً یک Task یا Task<T> برمیگرداند.
مثال ساده
شروع برنامه...
(۲ ثانیه مکث)
نتیجه دریافت شد:
دادههای نمونه بعد از ۲ ثانیه
برنامه تمام شد.
اجرای چند عملیات همزمان
}
ویژگیها در C#
- در C#، متدهای async معمولاً نوع بازگشتی Task یا Task<T> دارند.
- برای عملیات زمانبر مثل درخواست HTTP یا خواندن فایل مناسب است.
نکات مهم در C#:
- همیشه متدهای async باید Task یا Task<T> برگردانند، مگر در موارد خاص مثل event handlerها که void مجاز است.
- از await فقط در متدهای async میتوانید استفاده کنید.
چه زمانی از async استفاده کنیم؟
کلمه کلیدی async برای متدهایی استفاده میشود که شامل عملیات ناهمگام (asynchronous) هستند، یعنی عملیاتی که ممکن است زمانبر باشند و نباید رشته (thread) اصلی را مسدود کنند. این عملیات معمولاً شامل موارد زیر هستند:
- ورودی/خروجی (I/O): مثل درخواستهای HTTP، خواندن/نوشتن فایل، یا دسترسی به دیتابیس.
- عملیات زمانبر: مثل تأخیر (Task.Delay)، پردازشهای سنگین در سرور، یا انتظار برای پاسخ از API.
- کار با APIهای ناهمگام: متدهایی که از کتابخانههایی مثل HttpClient یا Entity Framework استفاده میکنند و خودشان بهصورت ناهمگام طراحی شدهاند.
چه زمانی نباید از async استفاده کنیم؟
- متدهای محاسباتی ساده (CPU-bound): اگر متد شما فقط شامل محاسبات синхронный (مثل حلقهها یا عملیات ریاضی) است و نیازی به انتظار برای عملیات I/O ندارد، استفاده از async معمولاً غیرضروری و حتی مضر است، چون سربار اضافی (overhead) ایجاد میکند. مثال نامناسب:
public async Task<int> CalculateSumAsync(int a, int b)
{
return a + b; // این عملیات نیازی به async ندارد
}
- این متد هیچ عملیات ناهمگامی ندارد، پس async فقط کد را پیچیدهتر میکند.
- متدهایی که خیلی سریع اجرا میشوند: اگر عملیات شما بسیار سریع است (مثلاً دسترسی به حافظه یا محاسبات سبک)، استفاده از async میتواند عملکرد را بدتر کند، چون مدیریت Task و context switching هزینهبر است.
مشکلات استفاده غیرضروری از async
- سربار عملکردی: استفاده از async باعث ایجاد سربار در مدیریت Taskها و state machine در کامپایلر میشود، که برای عملیات ساده غیرضروری است.
- پیچیدگی کد: کد async پیچیدهتر از کد سینکرونوس است و اگر بدون دلیل استفاده شود، خوانایی و نگهداری کد را سختتر میکند.
- خطاهای احتمالی: استفاده نادرست از async میتواند منجر به مشکلاتی مثل deadlock یا مدیریت نادرست exceptionها شود.
نکات کلیدی برای تصمیمگیری
- نوع عملیات را بررسی کنید:
- اگر متد شامل عملیات I/O یا انتظار (مثل await Task.Delay) است، از async استفاده کنید.
- اگر متد فقط محاسباتی است، معمولاً نیازی به async نیست مگر اینکه بخواهید آن را بهصورت غیرمسدودکننده در پسزمینه اجرا کنید (مثل Task.Run).
- نوع بازگشتی متد:
- متدهای async معمولاً باید Task یا Task<T> برگردانند. اگر متد شما void است و نیازی به انتظار ندارد، احتمالاً نیازی به async نیست (به جز در موارد خاص مثل event handlerها).
- استثنا: متدهای async void فقط برای event handlerها توصیه میشوند، چون نمیتوان آنها را await کرد.
- مدیریت منابع: در برنامههای با منابع محدود (مثل اپلیکیشنهای موبایل)، استفاده غیرضروری از async میتواند مصرف منابع را افزایش دهد.
بخش دوم: async و await در JavaScript
تعریف ساده
در جاوااسکریپت، متدهایی که async
باشند همیشه یک Promise برمیگردانند. با await
میتوان نتیجهی یک Promise را گرفت.
مثال ساده
async function fetchData() {
console.log("شروع برنامه...");
let result = await new Promise(resolve => {
setTimeout(() => resolve("دادههای نمونه بعد از ۲ ثانیه"), 2000);
});
console.log("نتیجه دریافت شد:");
console.log(result);
console.log("برنامه تمام شد.");
}
fetchData();
خروجی:
برنامه تمام شد.
اجرای چند عملیات همزمان
fetchMultiple();
خروجی:
[ 'داده لینک ۱', 'داده لینک ۲' ]
نکات کاربردی برای هر دو زبان
- مدیریت خطا: همیشه خطاها رو با try-catch مدیریت کنید تا برنامه پایدار بمونه.
- اجتناب از مسدود کردن: از متدهای ناهمگام (async) به جای متدهای همگام (مثل Thread.Sleep در C# یا حلقههای طولانی در JS) استفاده کنید.
- موازیسازی: برای اجرای چند عملیات ناهمگام، از Task.WhenAll (C#) یا Promise.all (JS) استفاده کنید.
- خوانایی کد: کدهای async رو ساده و خوانا نگه دارید و از پیچیدگی بیش از حد پرهیز کنید.
جمعبندی
- در C#، از async و await برای مدیریت عملیات زمانبر مثل درخواستهای HTTP یا I/O استفاده کنید. نوع بازگشتی معمولاً Task است.
- در JavaScript، از async/await برای کار با Promiseها (مثل fetch) استفاده کنید تا کد خواناتر و مدیریت عملیات ناهمگام سادهتر شود.
- با تمرین مثالهای بالا و ساخت پروژههای کوچک، میتونید تسلط خوبی به این مفاهیم پیدا کنید.