در دنیای JavaScript، یکی از مهمترین مفاهیم در برنامهنویسی مدرن، مدیریت کدهای ناهمزمان (Asynchronous Code) است. با معرفی async/await در ES2017، نوشتن کدهای ناهمزمان بسیار سادهتر و خواناتر شد.
۱. چرا به async/await نیاز داریم؟
قبل از async/await، توسعهدهندگان از callback و سپس از Promise برای مدیریت عملیات ناهمزمان استفاده میکردند. اما هر دو روش مشکلاتی داشتند:
- Callback Hell: تودرتو شدن بیش از حد توابع
- Promise Chain طولانی: سخت شدن خوانایی کد
به مثال زیر نگاه کنید:
fetchUser()
.then(user => fetchPosts(user.id))
.then(posts => fetchComments(posts[0].id))
.then(comments => console.log(comments))
.catch(error => console.error(error));
این کد کار میکند، اما خواندن و دیباگ آن سخت است. با استفاده از async/await میتوان همین منطق را به شکل ساده و قابلدرک نوشت.
۲. async و await دقیقاً چه هستند؟
دو کلمهی کلیدی جدید:
async: قبل از یک تابع نوشته میشود تا آن تابع را غیرهمزمان کندawait: فقط داخل توابع async قابل استفاده است و باعث توقف اجرا تا دریافت نتیجه میشود
۳. مثال ساده از async/await
async function fetchData() {
const response = await fetch("https://jsonplaceholder.typicode.com/posts/1");
const data = await response.json();
console.log(data);
}
fetchData();
- تابع fetchData با async تعریف شده، پس همیشه یک Promise برمیگرداند
- کلمهی await قبل از fetch باعث توقف اجرا تا دریافت پاسخ میشود
- سپس await response.json() تا آماده شدن دادهها منتظر میماند
- در نهایت داده چاپ میشود، بدون نیاز به .then()
۴. async همیشه Promise برمیگرداند
حتی اگر داخل تابع async هیچ Promise وجود نداشته باشد، خروجی آن خودبهخود در قالب Promise بازگردانده میشود:
async function sayHello() {
return "سلام دنیا!";
}
sayHello().then(msg => console.log(msg)); // سلام دنیا!
در واقع این دو برابرند:
async function f1() {
return "Hi";
}
function f2() {
return Promise.resolve("Hi");
}
۵. مدیریت خطاها در async/await
در Promiseها برای مدیریت خطا از .catch() استفاده میکنیم. اما در async/await میتوانیم از try...catch بهره ببریم:
async function getUserData() {
try {
const response = await fetch("https://jsonplaceholder.typicode.com/users/1");
const user = await response.json();
console.log(user);
} catch (error) {
console.error("خطا در دریافت اطلاعات:", error);
}
}
۶. اجرای همزمان چند Promise با Promise.all
گاهی نیاز داریم چند عملیات را بهصورت همزمان انجام دهیم. اگر از await پشتسرهم استفاده کنیم، هر خط منتظر قبلی میماند — که باعث کاهش کارایی میشود.
async function getData() {
const [users, posts] = await Promise.all([
fetch("https://jsonplaceholder.typicode.com/users").then(res => res.json()),
fetch("https://jsonplaceholder.typicode.com/posts").then(res => res.json())
]);
console.log(users.length, "کاربر");
console.log(posts.length, "پست");
}
getData();
۷. تفاوت async/await با Promise
| ویژگی | Promise | Async/Await |
|---|---|---|
| سینتکس | مبتنی بر .then() و .catch() | شبیه کد همزمان با try/catch |
| خوانایی | نسبتاً پیچیده در زنجیرهها | بسیار خوانا و تمیز |
| خطاها | با .catch() مدیریت میشود | با try...catch مدیریت میشود |
| کنترل جریان | سختتر در پروژههای بزرگ | سادهتر و ساختاریافتهتر |
۸. خطاهای رایج در استفاده از async/await
❌ ۱. استفاده از await خارج از تابع async
const data = await fetchData(); // خطا
✅ باید داخل تابع async باشد:
async function run() {
const data = await fetchData();
}
❌ ۲. فراموش کردن await قبل از Promise
const res = fetch("url");
const data = res.json(); // خطا
✅ نسخهی درست:
const res = await fetch("url");
const data = await res.json();
۹. نکتهی مهم درباره حلقهها
اگر در یک حلقه از await استفاده کنید، هر مرحله منتظر مرحلهی قبل میماند — یعنی اجرای ترتیبی دارد:
for (let id of [1, 2, 3]) {
await fetchUser(id); // ترتیبی: 1، سپس 2، سپس 3
}
اگر عملیاتها مستقل هستند و به ترتیب خاصی نیاز ندارند، بهتر است از Promise.all استفاده کنید:
await Promise.all([1, 2, 3].map(id => fetchUser(id))); // همزمان
۱۰. جمعبندی
Async/Await ابزاری قدرتمند و ساده برای مدیریت کدهای ناهمزمان در JavaScript است. با استفاده از آن میتوانید:
- منطقهای پیچیدهی Promise را به کدهای خوانا تبدیل کنید
- خطاها را راحتتر مدیریت کنید
- عملکرد برنامه را بهبود دهید
"بهترین راه یادگیری async/await تمرین در پروژههای واقعی است؛ مثلاً در فراخوانی API، خواندن فایلها (در Node.js)، یا ارتباط با دیتابیسها."
- توصیههای JavaScript Community
- همیشه از try/catch برای کنترل خطاها استفاده کنید
- از Promise.all برای افزایش کارایی بهره ببرید
- await را تنها زمانی استفاده کنید که واقعاً نیاز به توقف اجرا دارید