برنامهنویسی ناهمگام
همگام در برابر ناهمگام، Callback و Callback Hell، Event Loop و صفها، Promise و حالتهای آن، then/catch/finally، Promise.all و race و any و allSettled، Async/Await و مدیریت خطا.
همگام در برابر ناهمگام
همگام (Sync): هر خط بعد از خط قبلی تمام میشود — کل صفحه قفل است. ناهمگام (Async): عملیات طولانی در پسزمینه شروع و برنامه ادامه مییابد:
// همگام — همه به ترتیب:
console.log("اول");
console.log("دوم");
// ناهمگام — setTimeout:
console.log("شروع");
setTimeout(() => console.log("بعد از ۲ ثانیه"), 2000);
console.log("پایان");
// چاپ: شروع → پایان → بعد از ۲ ثانیه
Callback — روش قدیمی
تابعی که بعد از اتمام عملیات ناهمگام اجرا میشود:
function fetchData(callback) {
setTimeout(() => {
const data = { id: 1, name: "علی" };
callback(data);
}, 1000);
}
fetchData(data => console.log(data)); // بعد از ۱ ثانیه
Callback Hell — جهنم کالبک
عملیات زنجیرهای تو در تو میشود:
// نکابآلود! ❌
getUser(userId, (user) => {
getOrders(user.id, (orders) => {
getOrderDetails(orders[0], (details) => {
getProducts(details.items, (products) => {
console.log(products);
// بهم ریختگی ادامه دارد...
});
});
});
});
Promise — وعده
شیئی که نتیجه آینده عملیات ناهمگام را نمایندگی میکند:
const promise = new Promise((resolve, reject) => {
// عملیات ناهمگام:
setTimeout(() => {
const success = Math.random() > 0.5;
if (success) {
resolve("داده دریافت شد"); // fulfilled
} else {
reject(new Error("خطای شبکه")); // rejected
}
}, 1000);
});
// مصرف:
promise
.then(result => console.log(result)) // اگر موفق
.catch(error => console.log(error.message)); // اگر خطا
حالتهای Promise
| حالت | توضیح |
|---|---|
| pending | در انتظار — هنوز نتیجه نیامده |
| fulfilled | موفق — resolve صدا زده شد |
| rejected | ناموفق — reject صدا زده شد |
تغییر حالت یکباره و غیرقابل برگشت است.
then و catch و finally
fetch("/api/data")
.then(response => response.json()) // زنجیرهای!
.then(data => console.log(data))
.catch(error => console.log("خطا:", error))
.finally(() => console.log("همیشه — پاکسازی"));
هر
then یک Promise جدید برمیگرداند — این Zنجیره (Chaining) است. اگر در هر حلقه خطا رخ دهد، کنترل به نزدیکترین catch میرود.Promise.resolve و reject — ساخت سریع
const p1 = Promise.resolve(42); // بلافاصله fulfilled
const p2 = Promise.reject(new Error("x")); // بلافاصله rejected
p1.then(v => console.log(v)); // 42
Promise.all — همه با هم
همه باید موفق شوند — یکی خطا کند همه میسوزند:
const p1 = fetch("/api/users").then(r => r.json());
const p2 = fetch("/api/posts").then(r => r.json());
const p3 = fetch("/api/comments").then(r => r.json());
Promise.all([p1, p2, p3])
.then(([users, posts, comments]) => {
console.log("همه رسیدند!");
// users, posts, comments — به ترتیب
})
.catch(error => console.log("یکی خطا کرد:", error));
Promise.allSettled — نتیجه همه
همه تمام شوند، موفق یا ناموفق:
const promises = [
Promise.resolve("موفق"),
Promise.reject(new Error("ناموفق")),
Promise.resolve("دیگری")
];
Promise.allSettled(promises).then(results => {
results.forEach(r => {
if (r.status === "fulfilled") {
console.log("✓", r.value);
} else {
console.log("✗", r.reason.message);
}
});
});
// ✓ موفق | ✗ ناموفق | ✓ دیگری
Promise.race — اولین تمامشده
// اولین Promise که تمام شود میبرد — موفق یا خطا:
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error("Timeout!")), 3000)
);
const data = fetch("/api/slow").then(r => r.json());
Promise.race([data, timeout])
.then(result => console.log("قبل از timeout رسید"))
.catch(e => console.log(e.message)); // Timeout!
Promise.any — اولین موفق
// اولین fulfilled — خطاها نادیده (تا همه fail نشوند):
const servers = [
fetch("https://server1.com/api").then(r => r.json()),
fetch("https://server2.com/api").then(r => r.json()),
fetch("https://server3.com/api").then(r => r.json())
];
Promise.any(servers)
.then(fastest => console.log("سریعترین:", fastest))
.catch(e => console.log("همه سرورها fail شدند"));
Async Function — سینتکس مدرن
تابع async همیشه Promise برمیگرداند و await منتظر نتیجه میماند:
async function getData() {
return "داده"; // خودکار Promise.resolve("داده")
}
async function main() {
// کد ناهمگام مثل همگام:
const user = await fetch("/api/user").then(r => r.json());
const orders = await fetch("/api/orders/" + user.id).then(r => r.json());
console.log(orders);
// بجای Callback Hell:
// getUser → getOrders → getOrderDetails → همه خطی!
}
main();
مدیریت خطا با try/catch
async function loadProfile(userId) {
try {
const response = await fetch("/api/users/" + userId);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const user = await response.json();
const orders = await fetch(`/api/orders?user=${userId}`)
.then(r => r.json());
return { user, orders };
} catch (error) {
// هر await میتواند خطا بدهد:
console.error("خطا در بارگذاری:", error.message);
throw error; // دوباره پرتاب برای لایه بالا
} finally {
console.log("بارگذاری تمام شد");
}
}
Event Loop — حلقه رویداد
موتور اجرای جاوا اسکریپت — قلب ناهمگامی:
console.log("1 — همگام");
setTimeout(() => console.log("2 — Task (macrotask)"), 0);
Promise.resolve().then(() => console.log("3 — Microtask"));
console.log("4 — همگام");
// ترتیب: 1 → 4 → 3 → 2
// sync → micro → macro
مراحل اجرا:
- اجرای کد همگام (Call Stack)
- تخلیه کامل Microtask Queue (Promiseها)
- یک Task از Task Queue (setTimeout)
- تکرار — و رندر بین آنها
اجرای موازی با Promise.all + async
async function loadDashboard() {
// اشتباه — ترتیبی و کند: ❌
// const users = await getUsers();
// const posts = await getPosts();
// const stats = await getStats();
// مجموع ۳ ثانیه!
// درست — موازی: ✓
const [users, posts, stats] = await Promise.all([
getUsers(),
getPosts(),
getStats()
]);
// مجموع ۱ ثانیه — ماکزیمم!
return { users, posts, stats };
}
ساخت تابع sleep
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
async function demo() {
console.log("صبر...");
await sleep(2000);
console.log("۲ ثانیه گذشت!");
}
Async Iterator و for await
async function* generateNumbers() {
for (let i = 1; i <= 3; i++) {
await sleep(500);
yield i;
}
}
(async () => {
for await (const num of generateNumbers()) {
console.log(num); // 1 بعد 2 بعد 3 — هرکدام با فاصله
}
})();
جمعبندی — کدام ابزار؟
| ابزار | کاربرد |
|---|---|
async/await | کد خوانا — پیشفرض مدرن |
Promise.all | اجرای موازی — همه لازم |
Promise.allSettled | نتیجه همه — مهم نباشد fail |
Promise.race | Timeout، اولین پاسخ |
Promise.any | اولین موفق — redundancy |
این مبحث بخشی از دوره آموزش کامل جاوا اسکریپت است.