Promise و سه حالت آن: شناخت State پشت هر عملیات ناهمزمان

در درس‌های قبل بارها با fetch و متد then کار کرده‌اید، اما هنوز خود شیء Promise به‌طور مستقل بررسی نشده است. Promise در واقع شیئی است که نماینده‌ی نتیجه‌ی نهایی یک عملیات ناهمزمان است؛ نتیجه‌ای که ممکن است هنوز آماده نباشد، با موفقیت به پایان برسد، یا با شکست مواجه شود. این سه امکان دقیقاً همان سه State (حالت) یک Promise را تشکیل می‌دهند.

سه State یک Promise

هر Promise در طول عمر خود، دقیقاً یکی از این سه حالت را دارد:

State معنی
pending حالت اولیه؛ عملیات هنوز در حال انجام است و نتیجه‌ای مشخص نشده
fulfilled عملیات با موفقیت به پایان رسیده و مقداری در دسترس است
rejected عملیات با خطا مواجه شده است

نکته‌ی مهم این است که یک Promise پس از خروج از حالت pending، دیگر هرگز به آن بازنمی‌گردد و حالت نهایی آن (چه fulfilled و چه rejected) قابل تغییر نیست؛ به همین دلیل گفته می‌شود Promise پس از settle شدن، تغییرناپذیر (immutable) است.

ساخت دستی یک Promise

Promise با استفاده از سازنده‌ی Promise ساخته می‌شود که یک تابع اجراکننده (executor) می‌گیرد. این تابع دو پارامتر دارد: resolve برای اعلام موفقیت، و reject برای اعلام شکست:

ساخت دستی یک Promise
خروجی کنسول اینجا نمایش داده می‌شود...

بلافاصله پس از ساخت، Promise در حالت pending قرار دارد. تابع اجراکننده به‌صورت Synchronous و بلافاصله اجرا می‌شود، اما فراخوانی resolve یا reject داخل آن (که در این مثال درون یک setTimeout قرار دارد) می‌تواند به تعویق بیفتد.

تفاوت میان resolve/reject و then/catch

یکی از سردرگمی‌های رایج، خلط میان resolve/reject و then/catch است. resolve و reject توابعی هستند که سازنده‌ی Promise را به یکی از حالت‌های نهایی تغییر می‌دهند؛ در حالی که then و catch روشی برای واکنش‌نشان‌دادن به آن تغییر حالت از بیرون شیء Promise هستند:

مفهوم نقش محل استفاده
resolve / reject تغییر State داخلی Promise داخل تابع اجراکننده (executor)
then / catch واکنش به State نهایی از بیرون روی خود شیء Promise ساخته‌شده

Promise.resolve و Promise.reject

گاهی نیازی به سازنده‌ی کامل نیست و فقط باید یک Promise با مقدار مشخص از قبل ساخته شود. برای این منظور، دو متد ایستای Promise.resolve() و Promise.reject() استفاده می‌شوند:

const alreadyDone = Promise.resolve("مقدار آماده");
alreadyDone.then((value) => console.log(value)); // "مقدار آماده"

const alreadyFailed = Promise.reject(new Error("خطای از پیش تعیین‌شده"));
alreadyFailed.catch((error) => console.log(error.message));

نکات کلیدی

  • هر Promise در هر لحظه دقیقاً یکی از سه حالت pending، fulfilled یا rejected را دارد.
  • پس از settle شدن (fulfilled یا rejected)، حالت یک Promise دیگر قابل تغییر نیست.
  • resolve و reject حالت داخلی Promise را تغییر می‌دهند؛ then و catch از بیرون به آن تغییر واکنش نشان می‌دهند.
  • Promise.resolve و Promise.reject برای ساخت سریع یک Promise با نتیجه‌ی از پیش مشخص کاربرد دارند.

جمع‌بندی

شناخت سه State یک Promise—pending، fulfilled و rejected—پایه‌ی درک دقیق‌تر رفتار زنجیره‌های then/catch است که در درس ۲۱-۲ بررسی شد. تمایز میان resolve/reject به‌عنوان ابزار تغییر حالت از درون، و then/catch به‌عنوان ابزار واکنش از بیرون، یکی از مهم‌ترین نکاتی است که درک درست آن، از بسیاری از سردرگمی‌های رایج در کار با Promise جلوگیری می‌کند.