مقدمه
Promise یکی از مهمترین ابزارهای مدیریت عملیات غیرهمزمان در جاوااسکریپت است که با سادهتر کردن
مدیریت توالی عملیات، مدیریت خطا و خوانایی کدها را افزایش میدهد. Promise جایگزین callbackهای تو
در تو شده و کد شما را خواناتر و حرفهایتر میکند.
Promise یک شیء است که نتیجهی یک عملیات غیرهمزمان (مانند درخواست AJAX یا تایمر) را نمایندگی
میکند
و میتواند سه حالت داشته باشد: pending (در حال انتظار)، fulfilled (موفق) و
rejected (شکستخورده). در ادامه به جزئیات مربوط به شیء Promise خواهیم پرداخت.
ساختار یک Promise
در درس قبل دیدیم که callbackها میتوانند باعث پیچیدگی و کاهش خوانایی کد شوند. Promise با ارائه
یک ساختار استاندارد، این مشکل را حل میکند و به شما اجازه میدهد عملیات غیرهمزمان را به صورت
زنجیرهای و قابل مدیریت بنویسید.
Promise در جاوااسکریپت یک شیء است که نمایانگر نتیجهی نهایی یک عملیات غیرهمزمان است.
در واقع، Promise همانطور که از نامش پیداست، قول میدهد که در آینده یا نتیجهی یک عملیات را
برگرداند یا دلیل خطا را اعلام کند.
برای ساخت Promise جدید باید تابع سازنده آن را صدا بزنیم و داخل آن تابعی بسازیم که resolve و reject را دیافت کرده و
بسته به نتیجهی عملیات، Promise را resolve یا reject کنیم.
مثال زیر یک Promise را پس از دو ثانیه با موفقیت resolve میکند.
JAVASCRIPT
let p = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve("success!");
}, 2000);
});
p.then(function(result) {
console.log(result);
});
در اینجا ابتدا یک شیء Promise ساختهایم و آن را به متغیر p نسبت دادهایم.
تابعی که به Promise پاس شده، دو آرگومان دارد: یکی resolve که اگر عملیات
موفقیتآمیز باشد این تابع را صدا میزنیم و دیگری reject که در صورت شکست عملیات،
فراخوانی میشود. داخل این تابع هم از تابع setTimeout استفاده کردهایم تا یک
عملیات غیرهمزمان را شبیهسازی کنیم. بعد از 2 ثانیه تابعی که در آن
resolve("success") صدا زده شده، اجرا میشود و به این ترتیب، Promise با موفقیت به پایان
میرسد.
سپس، از متد then برای دریافت نتیجهی موفق Promise استفاده کردهایم. این متد یک
تابع
میگیرد که پارامتر آن همان مقداری است که با resolve داده شد. بعد از 2 ثانیه مقدار
"success" به این تابع داده شده و در کنسول چاپ میشود.
به طور خلاصه، در اینجا Promise پس از ۲ ثانیه resolve میشود و مقدار "success" را
به تابع then منتقل میکند.
اگر عملیات با خطا مواجه شود و reject فراخوانی شود، میتوانید با استفاده از متد
catch خطا را
مدیریت کنید:
JAVASCRIPT
let p = new Promise(function(resolve, reject) {
setTimeout(function() {
reject("error!");
}, 2000);
});
p.then(function(result) {
console.log(result);
}).catch(function(error) {
console.error(error);
});
در اینجا اگر به جای resolve، تابع reject را صدا بزنید، Promise وارد حالت
شکست میشود و میتوانید
با متد catch خطا را مدیریت کنید. این ساختار باعث میشود مدیریت خطاها در عملیات غیرهمزمان
بسیار سادهتر و قابل پیشبینیتر باشد.
چرخه اجرای کد Promise
وقتی یک Promise ساخته میشود، ابتدا در وضعیت pending قرار دارد.
کد درون تابع Promise که معمولاً یک عملیات غیرهمزمان مانند درخواست شبکه، کار با فایل یا
استفاده از setTimeout است، اجرا شده و بسته به موفقیت یا شکست عملیات، Promise در
یکی از
دو وضعیت fulfilled یا rejected قرار میگیرد. در وضعیت fulfilled، تابع resolve صدا زده شده
و
و مقدار داده شده به آن به اولین تابعی که با then ثبت شده، پاس میشود.
اگر بیش از یک تابع then تعریف شده باشد، یکی یکی اجرا میشوند و مقدار را دریافت میکنند.
اما در وضعیت rejected، تابع reject صدا زده شده و مقدار داده شده به آن به اولین تابعی که
با
catch ثبت شده، پاس داده میشود.
+-------------------------+
| Promise (pending) |
+-------------------------+
/ \
/ \
v v
+----------------+ +-----------------+
| resolve() | | reject() |
+----------------+ +-----------------+
| |
v v
+----------------+ +-----------------+
| fulfilled | | rejected |
+----------------+ +-----------------+
| |
v v
.then() handler .catch() handler
زنجیره Promise و مدیریت خطا
با Promise میتوان عملیات غیرهمزمان را به صورت زنجیرهای و بدون نیاز به تودرتوسازی نوشت
و این از مهمترین مزایایی است که Promise نسبت به توابع Callback دارد.
مثال زیر را ببینید.
JAVASCRIPT
new Promise(function(resolve, reject) {
setTimeout(() => resolve(5), 1000);
})
.then(x => x * 2)
.then(x => x + 1)
.then(console.log)
.catch(err => console.error(err));
این مثال، مقدار اولیه ۵ را دریافت و دو بار پردازش میکند. اگر هر مرحله خطا بدهد، catch اجرا
میشود. این شیوه خوانایی و کنترل خطا را بسیار سادهتر کرده است.
Promise.all و اجرای موازی
Promise.all یک متد استاتیک در جاوااسکریپت است که به شما اجازه میدهد چندین Promise را همزمان
اجرا کنید و زمانی که همهٔ آنها با موفقیت (resolve) به پایان رسیدند، نتیجه تمام آنها را با هم
دریافت کنید.
اگر همه Promiseها موفق شوند، Promise نهایی با آرایهای از نتایج resolve میشود.
اگر حتی یکی از Promiseها شکست بخورد (reject شود)، کل Promise نهایی فوراً رد میشود و مقدار خطا
همان خطای اولین Promise شکستخورده خواهد بود.
متد Promise.all دارای ساختار کلی زیر است:
JAVASCRIPT
Promise.all([promise1, promise2, promise3])
.then(results => {
})
.catch(error => {
});
در مثال زیر،
دو Promise داریم که هر دو با موفقیت resolve میشوند. Promise.all منتظر میماند تا هر دو به پایان
برسند و سپس نتایج را به صورت آرایهای به تابع then ارسال میکند. اگر یکی از Promiseها reject
شود، catch اجرا خواهد شد.
JAVASCRIPT
let p1 = Promise.resolve(3);
let p2 = Promise.resolve(7);
Promise.all([p1, p2]).then(function(results) {
console.log(results);
});
Promise.all منتظر میماند تا هر دو Promise کامل شوند و سپس آرایهای از نتایج را بازمیگرداند.
اگر هر کدام reject شود، کل Promise شکست میخورد.