فصل بیستم: کار با promiseها در اکما اسکریپت

31 شهریور 1397
es6-promise

Promiseها یکی از روش های بی نقص برای پیاده سازی برنامه نویسی غیرهمزمانی در جاوا اسکریپت است. قبل از Promise ها از کالبک ها برای پیاده سازی برنامه نویسی غیر همزمان استفاده می شد. برای شروع باید ببینیم که برنامه نویسی غیرهمزمان چیست و چطور می شود آن را با کالبک ها پیاده کرد.

کالبک (callback) چیست؟

یک تابع ممکن است به عنوان پارامتر به یک تابع دیگر ارسال شود. به این مکانیزم کالبک گفته می شود. یک کالبک اغلب در هنگام کار با رویدادها استفاده می شوند.

مثال زیر به درک بهتر این موضوع کمک می کند.

<script>   
   function notifyAll(fnSms, fnEmail) {   
      console.log('starting notification process');   
      fnSms();   
      fnEmail();   
   }   
   notifyAll(function() {   
      console.log("Sms send ..");   
   }, 
   function() {   
      console.log("email send ..");   
   });   
   console.log("End of script"); 
   //executes last or blocked by other methods   
</script>

در متد notifyAll() که در بالا نشان داده شده است، نوتیفیکیشن با ارسال ایمیل و sms اتفاق می افتد. در اینجا فراخوان متد notifyAll() دو متد را به عنوان پارامتر به آن پاس داده است. هر کدام از آن متدها یک وظیفه مانند ارسال ایمیل و ارسال sms را بر عهده دارد.

خروجی مثال بالا

starting notification process 
Sms send .. 
Email send .. 
End of script

در کد ذکر شده در بالا، فراخوانی متدها به صورت همزمان استفاده شده است. یعنی نخ (thread) رابط کاربری تا زمانی که کل فرآیند نوتیفیکیشن تکمیل شود، منتظر می ماند. فراخوانی همزمان را قفل کننده هم می گویند. حال می خواهیم درباره فراخوانی های غیرقفل کننده یا غیرهمزمان صحبت کنیم.

کالبک های غیرهمزمان چیست؟

مثال بالا را در نظر بگیرید. برای اینکه اسکریپت فراخوانی متد notifyAll() را به صورت غیرهمزمان اجرا کند باید از متد setTimeout() استفاده کنیم. این متد به صورت پیش فرض غیرهمزمان است.

متد setTimeout() دو پارامتر می گیرد:

  • یک متد کالبک
  • تعداد میلی ثانیه هایی که بعد از آن باید متد اجرا شود.

در این مورد پروسه نوتیفیکیشن داخل setTimeout قرار گرفته است. در اینجا 2 ثانیه تاخیر با کدنویسی تنظیم شده است. متد notifyAll() فراخوانی شده و نخ (thread) اصلی مانند متدهای دیگر اجرا می شود. در اینجا فرآیند نوتیفیکیشن باعث قفل شده thread اصلی جاوا اسکرپیت نمی شود.

<script>   
   function notifyAll(fnSms, fnEmail) {   
      setTimeout(function() {   
         console.log('starting notification process');   
         fnSms();   
         fnEmail();   
      }, 2000);   
   }   
   notifyAll(function() {   
      console.log("Sms send ..");   
   },  
   function() {   
      console.log("email send ..");   
   });   
   console.log("End of script"); //executes first or not blocked by others   
</script>

خروجی

End of script 
starting notification process 
Sms send .. 
Email send ..

در حالی که چندین کالبک داشته باشیم، کدها کمی ترسناک به نظر می رسند!

<script>   
   setTimeout(function() {   
      console.log("one");   
      setTimeout(function() {   
         console.log("two");   
         setTimeout(function() {   
            console.log("three");   
         }, 1000);   
      }, 1000);   
   }, 1000);   
</script>

ES6 با معرفی مفهوم promise کمک زیادی به شما می کند. Promiseها رویدادهای مستمر (continuation) دارند و به شما کمک می کنند تا چندین عملیات را به صورت غیرهمزمان انجام دهید.

مثال

اجازه دهید با یک مثال این مفهوم را توضیح بدهم.سینتکس آن در زیر آمده است:

var promise = new Promise(function(resolve , reject) {    
   // do a thing, possibly async , then..  
   if(/*everthing turned out fine */)    resolve("stuff worked");  
   else     
   reject(Error("It broke"));  
});  
return promise;
// Give this to someone

اولین قدم پیاده سازی promise ها ایجاد یک متد است که از promiseها استفاده خواهد کرد. با یک مثال این مفهوم را توضیح می دهیم. متد getSum() غیرهمزمان است، یعنی عملیات این متد نباید باعث قفل شدن اجرای متدهای دیگر شود. به محض اینکه عملیات تکمیل شد به فراخوان اطلاع می دهد. مثال زیر (قدم 1) یک آبجکت promise را تعریف کرده است (var promise).

متد سازنده promise دو تابع می گیرد که اولی هنگام اجرای موفقیت آمیز عملیات و دیگری در صورت شکست عملیات اجرا می شود. Promise نتیجه محاسبات را توسط کالبک resolve بر می گرداند.

قدم1: (resolve(n1+n2

اگر getSum() با یک خطا یا شرایط پیش بینی نشده ای مواجه شود، متد کالبک reject را در promise فراخوانی می کند و اطلاعات خطا را به فراخوان بر می گرداند.

قدم 2: reject(Error(“Negative not Supported”)

 پیاده سازی کدهای قدم 1 در زیر آمده است:

function getSum(n1, n2) {   
   varisAnyNegative = function() {   
      return n1 < 0 || n2 < 0;   
   }   
   var promise = new Promise(function(resolve, reject) {   
      if (isAnyNegative()) {   
         reject(Error("Negatives not supported"));   
      }   
      resolve(n1 + n2)
   });   
   return promise;   
}

جزئیات قدم 2 در حقیقت پیاده سازی فراخوان است. فراخوان باید از متد then که دو کالبک می گیرد استفاده کند.اولی هنگام موفقیت آمیز بودن و دومی برای موقعی که عملیات با شکست مواجه شده است اجرا می شود. هر متد یک پارامتر می گیرد که در مثال زیر نشان داده شده است.

getSum(5, 6)   
.then(function (result) {   
   console.log(result);   
},   
function (error) {   
   console.log(error);   
});

خروجی

11

چون نوع برگشتی getSum() یک promise است، ما می توانیم چندین دستور then  داشته باشیم. اولین then یک دستور برگشتی خواهد بود.

getSum(5, 6)   
.then(function(result) {   
   console.log(result);   
   returngetSum(10, 20); 
   // this returns another promise   
},   
function(error) {   
   console.log(error);   
})   
.then(function(result) {   
   console.log(result);   
}, 
function(error) {   
   console.log(error);
});

خروجی

11
30

در مثال زیر سه فراخوانی then با متد getSum() انجام شده است.

<script>   
   function getSum(n1, n2) {   
      varisAnyNegative = function() {   
         return n1 < 0 || n2 < 0;   
      }   
      var promise = new Promise(function(resolve, reject) {   
         if (isAnyNegative()) {   
            reject(Error("Negatives not supported"));   
         }   
         resolve(n1 + n2);   
      });   
      return promise;   
   }   
   getSum(5, 6)   
   .then(function(result) {   
      console.log(result);   
      returngetSum(10, 20); 
      //this returns another Promise   
   },   
   function(error) {   
      console.log(error);   
   })
   .then(function(result) {   
      console.log(result);   
      returngetSum(30, 40); 
      //this returns another Promise   
   }, 
   function(error) {   
      console.log(error);   
   })   
   .then(function(result) {   
      console.log(result);   
   }, 
   function(error) {         
      console.log(error);   
   });   
   console.log("End of script ");   
</script>

خروجی کد در زیر نمایش داده شده است

برنامه در  ابتدا عبارت end of script را نمایش می دهد و سپس نتایج فراخوانی متد getSum() یکی پس از دیگری چاپ می شود.

End of script  
11 
30 
70

در این مثال نشان دادیم که getSum() به سبک غیرهمزمان فراخوانی شده است. Promise ها یک روش عالی و بی نقص را برای تعامل با کالبک ها ارائه می دهند.

نویسنده شوید

دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.