promise در جاوا اسکریپت

در جاوا اسکریپت ، promise یک object است که مقداری را باز می گرداند که امیدوارید در آینده دریافت کنید ، اما نه در حال حاضر.با توجه به اینکه value در آینده با promise بازگردانده می شود ، promise برای مدیریت عملیات های ناهمزمان(asynchronous operations.) بسیار مناسب است.
درک مفهوم promise در جاوا اسکریپت با استفاده از تشبیه بسیار آسان تر است.
فرض کنید به خودتان قول داده اید که یادگیری جاوا اسکریپت(javascript) را تا ماه آینده تکمیل کنید.شما نمی دانید که در زمان و انرژی خود را برای یادگیری جاوا اسکریپت در ماه آینده صرف خواهید کرد و خواهید توانست یادگیری آن را کامل کنید.
promise دارای 3 حالت است:
- Pending: شما نمی دانید که آیا تا یک ماه آینده یادگیری جاوا اسکریپت را تکمیل می کنید یا خیر.
- Fulfilled: شما یادگیری جاوا اسکریپت را تا ماه آینده کامل می کنید.
- Rejected : شما اصلا جاوا اسکریپت را یاد نمی گیرید.
یک promise با جالت pending شروع می شود که نشان می دهد promise تکمیل نشده است و با حالت fulfilled (موفقیت) یا ejected (شکست) به پایان می رسد.
ایجاد یک Promise
برای ایجاد یک promise در جاوا اسکریپت باید از Promise
constructor استفاده کنید:
1 2 3 4 5 6 7 8 9 |
let completed = true; let learnJS = new Promise(function (resolve, reject) { if (completed) { resolve("I have completed learning JS."); } else { reject("I haven't completed learning JS yet."); } }); |
Promise
constructor یک تابع به عنوان argument قبول می کند.این تابع executor نام دارد.executor دوتابع به نام های ()resolve و ()reject قبول می کند.
زمانی که شما new Promise(executor) را فراخوانی می کنید، به صورت خودکار executor
فراخوانی می شود.
درون executor، شما به صورت دستی اگر executor با موفقیت تکمیل شد،تابع ()resolve و در صورت اتفاق افتادن error تابع ()reject را فراخوانی می کنید.
اگر کد جاوا اسکریپت فوق را در یک سند HTML قرار دهید و tab کنسول را بررسی کنید ، خواهید دید که promise در واقع resolved شده است زیرا مقدار متغیر completed
برابر با true است.
برای مشاهده وضعیت pending در promise، کدهای executor را درون ()setTimeout قرار دادیم.
1 2 3 4 5 6 7 8 9 10 11 |
let completed = true; let learnJS = new Promise(function (resolve, reject) { setTimeout(() => { if (completed) { resolve("I have completed learning JS."); } else { reject("I haven't completed learning JS yet."); } }, 3 * 1000); }); |
حالا شما می بینید که promise با وضعیت pending و مقدار undefined شروع می شود.مقدار promise بعدا و در زمانی که promise تکمیل شود،برگردانده می شود.
بعد از 3 ثانیه learnJS را در console تایپ کنید،خواهید دید که وضعیت promise به resolved تغییر کرده و مقدار promise برابر با رشته ای است که به تابع () resolveپاس داده شده است.
بنابراین فراخوانی تابع resolve شیء promise را به حالت fulfilled منتقل می کند. اگر مقدار متغیر completed
را به false تغییر دهید و دوباره اسکریپت را اجرا کنید:
1 |
let completed = false; |
با error مواجه می شوید و وضعیت promise پس از 3 ثانیه rejected
می شود:
به عبارت دیگر، فراخوانی تابع reject شیء promise را به حالت
rejected
منتقل می کند.
تصویر زیر حالات یک promise و تأثیر فراخوانی توابع ()resolve و ()reject را نشان می دهد:
هنگامی که promise به حالت fulfilled یا rejected رسید ، در آن حالت باقی می ماند و نمی تواند تغییر کند.
به عبارت دیگر ، promise نمی تواند از حالت fulfilled به حالت rejected تغییر یابد و بالعکس. همچنین نمی تواند از حالت fulfilled یا حالت rejected به حالت pending برگردد.
زمانی که یک Promise
ایجاد می شود،تا زمانی که resolved شود در حالت pending باقی می ماند.
برای زمانبندی استفاده از callback، زمانی که promise در واقع resolved یا rejected شد،باید متدهای Promise
object یعنی ()
then()
, catch()
, and finally
را فراخوانی کنید.
Consuming a Promise: then, catch, finally
1) The then()
method
متد ()
then
برای زمانبندی اجرای callback، زمانی که promise با موفقیت resolved شد، استفاده می شود.
متد ()
then
دو callback functions می گیرد:
1 |
promiseObject.then(onFulfilled, onRejected); |
اگر promise در حالت fulfilled باشد،onFulfilled
فراخوانی و اگر در حالت rejected باشد، onRejected
فراخوانی می شود.
تابع زیر یک Promise
object را برمی گرداند.
1 2 3 4 5 6 7 8 9 10 11 |
function makePromise(completed) { return new Promise(function (resolve, reject) { setTimeout(() => { if (completed) { resolve("I have completed learning JS."); } else { reject("I haven't completed learning JS yet."); } }, 3 * 1000); }); } |
قطعه کد زیر،تابع ()makePromise و متد ()then را فراخوانی می کند.
1 2 3 4 5 6 |
let learnJS = makePromise(true); learnJS.then( success => console.log(success), reason => console.log(reason) ); |
این امکان وجود دارد که یکcallback را برای حالت fulfilled یا rejected برنامه ریزی کنید. کد زیر حالت fulfilled را اجرا می کند:
1 2 3 |
learnJS.then( value => console.log(value) ); |
و کد زیر حالت rejected را اجرا می کند:
1 2 3 4 5 6 |
let masterJS = makePromise(false); masterJS.then( undefined, reason => console.log(reason) ); |
توجه داشته باشید که باید undefined
را به عنوان اولین argument به متد ()then پاس دهیم.
اگر شما می خواهید یک callback در حالتی که promise در وضعیت rejected است،اجرا شود، می توانید از() catch استفاده کنید.
1 2 3 |
learnJS.catch( reason => console.log(reason) ); |
3) The finally()
method
گاهی اوقات شما می خواهید یک قطعه کد را بدون در نظرگرفتن اینکه promise در وضعیت fulfilled یا rejected است،اجرا کنید.برای مثال:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
function createApp() { // ... } learnJS .then( (success) => { console.log(success); createApp(); } ).catch( (reason) => { console.log(reason); createApp(); } ); |
همانطور که مشاهده می کنید،فراخوانی تابع ()createApp در هر دو متد ()then و ()catch تکرار شده است.
برای حذف این تکرار بدون در نظرگرفتن وضعیت promise، می توانید از متد ()finally مشابه زیر استفاده کنید:
1 2 3 4 |
learnJS .then(success => console.log(success)) .catch(reason => console.log(reason)) .finally(() => createApp()); |
مثال عملی از Promise در جاوا اسکریپت
در ادامه نحوه load یک فایل JSON از سرور و نمایش آن در صفحه وب را کار خواهیم کرد.
فرض کنید که ما فایل JSON را در آدرس زیر داریم:
https://www.reactappp.ir/sample/promise/api.json
و محتوای فایل json به شکل زیر است:
1 2 3 |
{ "message": "JavaScript Promise Demo" } |
قطعه کد زیر یک فایل HTML که دارای یک button است را نمایش می دهد.زمانی که بر روی button کلیک کنید،اطلاعات از فایل json بارگزاری می شود و message نمایش داده می شود.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>JavaScript Promise Demo</title> <link href="css/style.css" rel="stylesheet"> </head> <body> <div id="container"> <div id="message"></div> <button id="btnGet">Get Message</button> </div> <script src="js/promise-demo.js"> </script> </body> </html> |
برای load فایل json از XMLHttpRequest استفاده می کنیم.شما می توانید همچنین از fetch api استفاده کنید.
تابع زیر یک Promise object که data را از فایل json بارگزاری می کند،برمی گرداند.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
function load(url) { return new Promise(function (resolve, reject) { const request = new XMLHttpRequest(); request.onreadystatechange = function (e) { if (this.readyState === 4) { if (this.status == 200) { resolve(this.response); } else { reject(this.status); } } } request.open('GET', url, true); request.send(); }); } |
درون executor، در صورتی که HTTP status code برابر با 200 باشد، تابع ()resolve را فراخوانی و response را به آن پاس می دهیم.در غیراینصورت، تابع() reject را فراخوانی و HTTP status code را به آن پاس می دهیم.
برای button یک eventListener برای onClick مشخص می کنیم که متد ()then را در Promise فراخوانی می کند.اگر load فایل با موفقیت انجام شود،message برگشتی از سرور را نمایش می دهیم.در غیر اینصورت، پیغام error را با HTTP status code نمایش می دهیم.
1 2 3 4 5 6 7 8 9 10 11 12 |
const btn = document.querySelector('#btnGet'); const msg = document.querySelector('#message'); btn.onclick = function () { load('https://www.javascripttutorial.net/sample/promise/api.json') .then( response => { const result = JSON.parse(response); msg.innerHTML = result.message; }, error => msg.innerHTML = `Error getting the message, HTTP status: ${error}` ); } |
دیدگاهتان را بنویسید