طبق تعریف موزیلا، آبجکت promise برای مدیریت محاسبات غیرهمزمان در مواقعی که مدیریت محاسبات توسط متدهای کالبک مشکل باشد، استفاده می شود.
Promise ها متدهایی را برای کنترل مقادیر، در حین انجام موفقیت آمیز یا غیرموفقیت آمیز یک عملیات، ارائه می کنند.
Promise با ما این امکان را می دهد تا در مواقع موفقیت یا شکست یک عملیات غیرهمزمان، یک قابلیتی را به اجرا در آوریم. همچنین این اجازه را می دهد تا این گونه سناریوهای پیچیده را با کدهای معمولی (همزمانی) پیاده سازی کنیم.
برای مثال، به کدهای همزمانی زیر که مقدار زمان جاری را در خروجی چاپ می کند، توجه کنید:
var currentTime = new Date(); console.log('The current time is: ' + currentTime);
کدهای فوق خیلی سرراست است و مقدار آبجکت ()new Date
که زمان جاری است را در مرورگر نشان می دهد. حال فرض کنید که ما از ساعت های مختلف بر روی تعدادی از سرورهای از راه دور استفاده می کنیم.
برای مثال، اگر یک ساعت تبریک سال نو را در نظر بگیرید، خیلی خوب است که بتوانیم مرورگر کاربران را با یکدیگر با استفاده از یک مقدار زمانی واحد، همگام کنیم.
فرض کنید متدی به نام ()getCurrnetTime
داریم که مقدار زمان جاری را از یک سرور از راه دور دریافت می کند. سپس توسط متد ()setTimeOut
این متد را بعد از 2 ثانیه اجرا می کنیم (در حقیقت با اینکار دریافت داده ها از یک API را شبیه سازی می کنیم).
function getCurrentTime() { // Get the current 'global' time from an API return setTimeout(function() { return new Date(); }, 2000); } var currentTime = getCurrentTime() console.log('The current time is: ' + currentTime);
مقداری که console.log نشان می دهد، قطعاً زمان جاری نیست. معمولا توسط یک کالبک مقدار زمان را بروزرسانی می کنیم:
function getCurrentTime(callback) { // Get the current 'global' time from an API return setTimeout(function() { var currentTime = new Date(); callback(currentTime); }, 2000); } getCurrentTime(function(currentTime) { console.log('The current time is: ' + currentTime); });
اما اگر خطایی در حین عملیات اتفاق بیفتد، باید چکار کنیم؟ چطور باید خطا را مدیریت کنیم؟
function getCurrentTime(onSuccess, onFail) { // Get the current 'global' time from an API return setTimeout(function() { // randomly decide if the date is retrieved or not var didSucceed = Math.random() >= 0.5; if (didSucceed) { var currentTime = new Date(); onSuccess(currentTime); } else { onFail('Unknown error'); } }, 2000); } getCurrentTime(function(currentTime) { console.log('The current time is: ' + currentTime); }, function(error) { console.log('There was an error fetching the time'); });
حال، اگر بخواهیم یک درخواست بر اساس مقدار اولین درخواست ایجاد کنیم، باید چکار کنیم؟
به عنوان مثال، اجازه بدهید دوباره از متد ()getCurrentTime
مطابق زیر استفاده کنیم:
function getCurrentTime(onSuccess, onFail) { // Get the current 'global' time from an API return setTimeout(function() { // randomly decide if the date is retrieved or not var didSucceed = Math.random() >= 0.5; console.log(didSucceed); if (didSucceed) { var currentTime = new Date(); onSuccess(currentTime); } else { onFail('Unknown error'); } }, 2000); } getCurrentTime(function(currentTime) { getCurrentTime(function(newCurrentTime) { console.log('The real current time is: ' + currentTime); }, function(nestedError) { console.log('There was an error fetching the second time'); }) }, function(error) { console.log('There was an error fetching the time'); });
در این روش، کار کردن با عملیات غیرهمزمان کمی دشوار است. همچنین می توانیم مقادیر را از فراخوانی تابع قبلی بدست بیاوریم. گاهی اوقات به مسائلی برخورد می کنیم که هنگام شروع برنامه هنوز بعضی از داده ها دریافت نشده اند.
با استفاده از promise ها می توانیم بر بسیاری از پیچیدگی های فوق غلبه کنیم. برای مثال می توان کدهای بالا را توسط promise مطابق زیر بازنویسی کرد:
function getCurrentTime(onSuccess, onFail) { // Get the current 'global' time from an API using Promise return new Promise((resolve, reject) => { setTimeout(function() { var didSucceed = Math.random() >= 0.5; didSucceed ? resolve(new Date()) : reject('Error'); }, 2000); }) } getCurrentTime() .then(currentTime => getCurrentTime()) .then(currentTime => { console.log('The current time is: ' + currentTime); return true; }) .catch(err => console.log('There was an error:' + err))
همان طور که می بینید کدهای فوق تمیزتر به نظر می رسد و همچنین مشکلاتی که در هنگام کار با عملیات غیرهمزمان به وجود می آید را ندارد. در صورت موفقیت آمیز بودن عملیات از تابع ()then
بر روی یک نمونه از آبجکت promise استفاده می کنیم. همچنین برای دسترسی به مقدار برگشتی promise از متد ()then
استفاده می کنیم. برای نمونه در مثال فوق، مقدار تابع ()getCurrentTime
در currentTime قرار گرفته و در صورت موفقیت آمیز بودن عملیات، آن مقدار در خروجی چاپ می شود. برای کنترل خطاهای احتمالی که ممکن است در این زنجیره promise اتفاق بیفتد، از متد ()catch
استفاده می شود.
در مثال بالا از یک زنجیره promise برای ساخت دنباله ای از کارها که یکی پس از دیگری باید اجرا شود، استفاده کردیم. در نگاه اول promise کمی پیچیده به نظر می رسد، اما در حقیقت کار با آنها خیلی راحت است.همچنین در صورت موفقیت آمیز بودن یک عملیات می توانیم از کدهای معمولی برای کنترل کارهای غیرهمزمان استفاده کنیم.
برای مثال اگر بخواهیم مقدار برگشتی فراخوانی تابع ()getCurrentTime
را تغییر دهیم، می توانیم مانند زیر یک ارتباط بین زنجیرها برقرار کنیم:
getCurrentTime() .then(currentTime => getCurrentTime()) .then(currentTime => { return 'It is now: ' + currentTime; }) // this logs: "It is now: [current time]" .then(currentTimeMessage => console.log(currentTimeMessage)) .catch(err => console.log('There was an error:' + err))
یک promise در یک زمان تنها می تواند یکی از حالت های زیر را داشته باشد:
یک آبجکت معلق (Pending) می تواند در یکی از حالت های انجام شده (resolved) یا رد شده (error) و تنها یکبار اتفاق بیفتد و در بعضی از مواقع که ممکن است خطاهای پیچیده در برنامه اتفاق بیفتد، کاربرد دارد. در این شرایط، هر حالت که اتفاق بیفتد یک promise را بر می گردانیم. اگر بخواهیم مقداری را از تابعی که از promise استفاده می کند را برگردانیم، باید promise جدیدی را ایجاد کنیم.
با استفاده از متد سازنده promise می توانیم یک promise جدید ایجاد کنیم. این متد سازنده یک تابع با دو پارامتر می گیرد، که عبارتند از:
در مثال بالا، اگر درخواست با موفقیت اجرا شود متد ()resolve
و اگر با خطا مواجه شود متد ()reject
اجرا می شود.
var promise = new Promise(function(resolve, reject) { // call resolve if the method succeeds resolve(true); }) promise.then(bool => console.log('Bool is true'))
حال که فهیدیم promise چیست و چطور کار می کند، می توانیم از کتابخانه fetch که در درس بعدی ارائه خواهیم داد، استفاده کنیم.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.