منابع آموزشی و پژوهشی، برای دانشگاه و مدرسه
پشتیبانیانتشار محتوا
مسیر یادگیری

جاوااسکریپت ۲؛ دورهٔ متوسط و پروژه‌های کاربردی

دورهٔ دوم و رایگان جاوااسکریپت، تألیف تحریریه درس‌آوند؛ ادامهٔ دورهٔ «جاوااسکریپت از صفر تا پروژه». ۲۴ درس سطح متوسط با مثال عملی، تمرین و پاسخ و سه پروژهٔ مدیریت هزینه، فروشگاه و API محلی تیکت. پیش‌نیاز: تسلط بر مبانی دورهٔ اول و HTML مقدماتی. زمان هر درس برآورد مطالعه و تمرین است؛ این نسخه ویدیو ندارد.

۲۴ درس۱٬۱۰۰ دقیقه۰ درس کامل‌شده
پرسش دربارهٔ این دوره
اجرای غیرهم‌زمان و فروشگاه · درس ۱۳

۱۳. اجرای غیرهم‌زمان؛ Event Loop، میکروتسک و ترکیب Promise

دورهٔ رایگانثبت‌نام و پیشرفت یادگیری در حساب شما نگهداری می‌شود.ورود / ساخت حساب

در سطح متوسط باید بتوانید ترتیب اجرای کد را پیش‌بینی کنید و برای عملیات مستقل یا وابسته ساختار مناسب انتخاب کنید. async به معنی اجرای موازی همهٔ کدها نیست. یک حلقهٔ محاسباتی طولانی در همان رشته هنوز می‌تواند کلیک و رندر را معطل کند. Promise ابزار توصیف نتیجهٔ آینده است؛ به‌تنهایی کار سنگین را به رشتهٔ دیگری منتقل نمی‌کند.

مثال اول؛ ترتیب را قبل از اجرا بنویسید

مثال را در Console یک صفحهٔ عادی مرورگر اجرا کنید. پیش از اجرا روی کاغذ بنویسید کدام پیام اول و کدام آخر است. این مثال به شبکه وابسته نیست؛ بنابراین برای فهمیدن صف‌ها نتیجهٔ قابل تکرارتری دارد.

javascript
console.log('A: شروع');
setTimeout(() => console.log('D: تایمر'), 0);
Promise.resolve().then(() => console.log('C: میکروتسک'));
console.log('B: پایان کد هم‌زمان');
// A سپس B سپس C سپس D

کد فعلی تا پایان اجرا می‌شود؛ callbackِ then در میکروتسک و callback تایمر در یک task بعدی اجرا می‌شود. صفر در setTimeout به معنی اجرای فوری نیست. زمان واقعی می‌تواند بیشتر شود، چون صف و شرایط محیط اثر دارند. نتیجهٔ این مثال را به ترتیب همهٔ APIهای Node تعمیم ندهید؛ این درس مدل مرورگر را بررسی می‌کند.

از این ترتیب یک پیام عملی بگیرید: اگر بعد از تغییر متن دکمه یک محاسبهٔ بسیار طولانی انجام دهید، لزوماً کاربر قبل از محاسبه متن تازه را نمی‌بیند. همچنین زنجیرهٔ بی‌پایان میکروتسک می‌تواند فرصت taskهای بعدی را بگیرد. راه‌حل کار سنگین، انتخاب ساختار مناسب، تقسیم سنجیدهٔ کار یا در موارد لازم Worker است؛ گذاشتن کلمهٔ async جلوی تابع درمان توقف رابط نیست.

مثال دوم؛ کار مستقل را پشت هم معطل نکنید

فرض کنید داشبورد، فهرست هزینه و نام دسته‌ها را از دو منبع مستقل می‌خواند. در مثال زیر منبع‌ها ساختگی‌اند تا رفتار را بدون اینترنت بررسی کنیم. کد را در یک فایل .mjs با Node یا در Console مرورگری با پشتیبانی top-level await اجرا کنید.

javascript
const later = (value, ms) => new Promise(resolve => {
  setTimeout(() => resolve(value), ms);
});
const expensesPromise = later([{ amount: 180000 }], 40);
const categoriesPromise = later(['آموزش', 'خوراک'], 10);
const [expenses, categories] = await Promise.all([
  expensesPromise, categoriesPromise
]);
console.log(expenses[0].amount, categories[0]); // 180000 آموزش

ترتیب آرایهٔ خروجی، ترتیب ورودی Promise.all است و به ترتیب تمام شدن وابسته نیست. کارها هنگام فراخوانی later آغاز شده‌اند؛ Promise.all آن‌ها را آغاز نمی‌کند، نتیجه‌هایشان را ترکیب می‌کند. برای عملیات وابسته چنین ترکیبی درست نیست: اگر ابتدا باید شناسهٔ تیکت ساخته شود، درخواست پیام همان تیکت نمی‌تواند پیش از دریافت شناسه آغاز شود.

همهٔ شکست‌ها یک تصمیم ندارند

در صفحهٔ داشبورد شاید شکست یک بخش نباید بخش دیگر را حذف کند. Promise.all با اولین رد شدن، رد می‌شود، ولی بقیهٔ عملیات را خودکار لغو نمی‌کند. allSettled برای گزارش مستقل نتیجه‌ها مناسب‌تر است. نمونهٔ زیر ادامهٔ مستقلی برای تمرین همین تصمیم است؛ خطای ساختگی را عمداً می‌سازیم.

javascript
const outcomes = await Promise.allSettled([
  Promise.resolve({ count: 3 }),
  Promise.reject(new Error('گزارش موقتاً در دسترس نیست'))
]);
for (const item of outcomes) {
  if (item.status === 'fulfilled') console.log('داده:', item.value);
  else console.log('خطا:', item.reason.message);
}

در پروژهٔ تیکت، try/catch باید مرز عملیات قابل توضیح را پوشش دهد. catch خالی فقط خطا را ناپدید می‌کند و ممکن است رابط در حالت انتظار بماند. در finally می‌توان کنترل‌ها را آزاد کرد، اما برای درخواست‌های هم‌پوشان باید مطمئن شد finally درخواست قدیمی وضعیت درخواست جدید را خراب نمی‌کند. درس بعد با شناسهٔ درخواست همین مشکل را حل می‌کند.

تمرین مستقل و پاسخ تشریحی

سه گزارش ساختگی بسازید که یکی از آن‌ها شکست می‌خورد. ابتدا با Promise.all و سپس با allSettled اجرا کنید و تفاوت اطلاعات قابل نمایش را بنویسید. سپس نسخه‌ای بسازید که عملیات دوم واقعاً به خروجی اول وابسته است. پاسخ راهنما: برای گزارش مستقل، هر outcome به وضعیت همان بخش تبدیل می‌شود؛ برای وابستگی، await اول قبل از فراخوانی عملیات دوم لازم است. زمان اجرای دقیق را معیار آزمون قرار ندهید؛ ترتیب و نتیجه معیار بهتری است.

معیار تحویل: ترتیب A تا D را توضیح دهید؛ ثابت کنید all نتایج را به ترتیب ورودی برمی‌گرداند؛ نشان دهید رد شدن all به معنی لغو بقیه نیست؛ برای خطای هر عملیات پیام معنادار داشته باشید. اگر تعداد زیادی درخواست دارید، همه را بی‌محدودیت آغاز نکنید؛ کنترل تعداد عملیات هم‌زمان مرحلهٔ بعدی طراحی است.

منابع انگلیسی برای مطالعهٔ تکمیلی

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Execution_model
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled

تألیف و مثال‌های مستقل: تحریریه درس‌آوند.

یکجا انتخاب کن، راحت‌تر دریافت کن

انتخاب‌های من ۰ منبع

مجموعهٔ مطالعه‌ات را بساز.

کنار مقاله‌ها «انتخاب» را بزنید تا چند فایل را با هم دریافت کنید.

انتخاب مقاله‌های رایگان