برنامه‌نویسی ناهمگام

همگام در برابر ناهمگام، 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

مراحل اجرا:

  1. اجرای کد همگام (Call Stack)
  2. تخلیه کامل Microtask Queue (Promiseها)
  3. یک Task از Task Queue (setTimeout)
  4. تکرار — و رندر بین آن‌ها

اجرای موازی با 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.raceTimeout، اولین پاسخ
Promise.anyاولین موفق — redundancy

این مبحث بخشی از دوره آموزش کامل جاوا اسکریپت است.

ابزارهای مرتبط