جواب کوتاه: کد همزمان (sync) همیشه اول اجرا میشود، بعد microtaskها (نتیجهی Promiseها)، و در آخر macrotaskها (setTimeout, setInterval) — این ترتیب، نه شانسی است و نه به ترتیب نوشتن کد بستگی دارد.
یک مثال که ترتیبش عجیب به نظر میرسد
jsconsole.log('1: شروع');
setTimeout(() => console.log('4: setTimeout'), 0);
Promise.resolve().then(() => console.log('3: promise.then'));
console.log('2: پایان کد همزمان');
1: شروع
2: پایان کد همزمان
3: promise.then
4: setTimeout
با اینکه setTimeout با تأخیر ۰ نوشته شده و قبل از Promise.resolve() هم آمده، بعد از آن اجرا میشود. جاوااسکریپت اول تمام کد همزمان را تا انتها اجرا میکند (خط ۱ و ۲)، بعد صف microtask را خالی میکند (.then)، و فقط در آخر سراغ صف macrotask میرود (setTimeout)، حتی با تأخیر صفر.
async فقط یک Promise برمیگرداند
jsasync function f() { return 5; }
const r = f();
console.log(r); // Promise { 5 }
r.then(v => console.log('resolved:', v));
console.log('این خط قبل از resolve شدن پرینت میشود');
Promise { 5 }
این خط قبل از resolve شدن پرینت میشود
resolved: 5
هر async function، مهم نیست چه چیزی return کند، همیشه یک Promise برمیگرداند. حتی return 5 هم Promise { 5 } میشود، نه خودِ عدد ۵.
await فقط همان تابع را متوقف میکند، نه کل برنامه را
jsasync function work() {
console.log('A: شروع کار async');
await new Promise(res => setTimeout(res, 50));
console.log('C: بعد از await');
}
work();
console.log('B: این وسط اجرا میشود، بدون توقف');
A: شروع کار async
B: این وسط اجرا میشود، بدون توقف
C: بعد از await
await اجرای تابع work را نگه میدارد، ولی بقیهی برنامه (خط B) منتظرش نمیماند و بلافاصله اجرا میشود. این دقیقاً همان چیزی است که جاوااسکریپت را non-blocking نگه میدارد.
گیر رایج: await داخل forEach
js[300, 100, 200].forEach(async (ms) => {
await delay(ms, ms);
});
console.log('این خط بلافاصله پرینت میشود، بدون صبر کردن');
این خط بلافاصله پرینت میشود، بدون صبر کردن
انجام شد: 100
انجام شد: 200
انجام شد: 300
forEach منتظر Promiseهایی که callbackاش برمیگرداند نمیماند — چون خروجی callback را اصلاً دور میریزد. اگر واقعاً به «صبر کردن تا همه تمام شوند» نیاز داری، باید از for...of یا Promise.all استفاده کنی.
دو راه درست: یکییکی یا همزمان
js// یکییکی — هرکدام منتظر قبلی میماند
for (const ms of [300, 100, 200]) {
await delay(ms, ms);
}
// انجام شد: 300, 100, 200 — به همین ترتیب
js// همزمان — همه باهم شروع میشوند، ولی کد صبر میکند تا همه تمام شوند
const results = await Promise.all([300, 100, 200].map(ms => delay(ms, ms)));
console.log(results); // [300, 100, 200]
نکتهی ظریف اینجاست: با Promise.all، هرکدام زودتر تمام شود زودتر لاگ میشود (۱۰۰ اول تمام میشود)، ولی آرایهی نتیجه همیشه به ترتیب همان آرایهی ورودی است (۳۰۰، ۱۰۰، ۲۰۰)، نه به ترتیب اتمام.
اگر میخواهید جاوااسکریپت را از پایه و با تمرین واقعی یاد بگیرید — از مقدارهای اولیه تا همین حلقهی رویداد — دورهی جاوااسکریپت از صفر دقیقاً همین رفتارها را قدمبهقدم تمرین میدهد.





نظرها
اولین نفری باشید که نظر میدهدسؤالتان را بپرسید یا تجربهتان را بنویسید — نویسندهی مطلب پاسخ میدهد.