کال بک (callback) در جاوا اسکریپت

ابتدا نیاز می بینم که با ویژگی هایی که نوع فانکشن ها در جاوااسکریپت دارند آشنا بشویم.

در جاوا اسکریپت فانکشن ها first class هستند! در واقع از نوع object هستند.
First Class به موجودیتی می گویند که می تواند داینامیک ساخته شود، به عنوان آرگومان پاس داده شود به عنوان مقدار برگشت داده شده و در نهایت حذف و نابود شوند. در یک جمله "هیچ محدودیتی در استفاده از آنها ندارید". تابع هایی که به این صورت هستند higher-order functions نامیده می شوند.


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

  • با فانکش ها مانند نوع داده های دیگر با آنها برخورد می شود
  • فانکشن ها از نوع object type هستند
  • شما می توانید فانکشن ها را در یک متغیر ذخیره کنید
  • فانکشن می تواند دارای property هایی باشد و همچنین link back به متد سازنده خودش!
  • شما می توانید فانکشن ها رو به عنوان آرگومان به فانکش دیگر ارسال نمائید
  • می توانید آنها در داخل فانکش دیگر تعریف و دستکاری کنید
  • شما می توانید آنها را به راحتی در داخل فانکش دیگر فراخوانی کنید
  • شما می توانید فانکشن ها رو به عنوان مقدار بازگشتی یک فانکش بر گردونید

همین موارد بالا ماهیت استفاده از فانکشن های callback هستند.

فانکشن callback چیست ؟

فانکش callback تابعی است که قرار است بعد از تابع دیگری اجرا شود.

چرا به callback function ها نیاز داریم ؟

جاوا اسکریپت یک زبان رویداد گرا است بدین معنی که ب جای صبر کردن برای دریافت پاسخ و سپس ادامه اجرا، به سراغ کد بعدی برای اجرا می رود و در صورتی که در همین مدت به event های دیگر listen می کند. و در صورتی که پاسخ آنها آماده شده باشد دوباره به سراغ اجرای آنها خواهد رفت. در واقع کارها را به صورت asynchronous یا همان غیر همزمان انجام می دهد.

برای روشن تر شدن مطلب بگذارید مثالی را بیان کنیم.

function first(){
  console.log(1);
}

function second(){
  console.log(2);
}

first();
second();
// 1
// 2

در قطعه کد بالا همانطور که انتظار می رود فانکشfirst اول و سپس فانکشن second اجرا می شود.

خب حالا اگر فانکشن first قطعه کدی بود که سریعا اجرا و جواب را نمی داد چه ؟ مثل ارسال اطلاعات به یک API و دریافت جواب.

برای شبیه سازی این حالت من از یکی از توابع native جاوااسکریپت به نا setTimeOut استفاده میکنم که بعد از مدت زمان مشخصی تابع را call می کند.

کد جدید ما به اینحالت می باشد:

function first(){
  // Simulate a code delay
  setTimeout( function(){
    console.log(1);
  }, 500 );
}

function second(){
  console.log(2);
}

first();
second();

الان مهم نیست که شما نحوه کار تابع setTimeOUT را متوجه می شوید یا نه!( هر چند برای آن این لینک مفید رو معرفی می کنم)

چیزی که الان مهمه این است که ما (1)console.log رو به داخل delay 500 میلی ثانیه ای خودمون انتقال دادیم.

خب حالا اگر فانکشن های خودمون رو صدا بزنیم، خروجی چه خواهد بود ؟

first();
second();
// 2
// 1

اگر چه ما تابع first رو ابتدا صدا زدیم ولی در نهایت خروجی آن بعد از تابع second فراخوانی شد. این بدین معنا نیست که جاوا اسکرسپت کدهای ما رو به ترتیبی که ماخواسته بودیم اجرانکرده! بلکه جاوا اسکریپ منتظر دریافت پاسخ از تابع first نمونده و به سراغ اجرای تابع second رفته است.

شما نمی تونید فانکشن ها رو به ترتیب پس از هم صدا بزنید و انتظار داشته باشید که به همان ترتیب اجرا شوند. پس callback ها برای این هستند که مطمن شویم قطعه کد مشخصی حتما بعد از اتمام اجرای کد دیگر اجرا می شود. فانکشن های callback برای مواردی مثل درخواست AJAX یا API ها که مدت زمانی را برای ارسال و دریافت پاسخ طول می کشد و باید منتظر باشیم خیلی کاربرد دارد.

فانکشن های callback برای این که ما مطمئن شویم تا قطعه کدی اجرا نمی شود مگر اینکه کد دیگری اجراش تمام شده باشد کاربرد دارد.

بیاید callback ایجاد کنیم!

function doHomework(subject) {
  alert(`Starting my ${subject} homework.`);
}

فانکشن doHomework ما یک موضوع رو به عنوان آرگومان میگیره و اون رو به عنوان تکلیف انجام میدهد ! مثلا:

doHomework('math');
// Alerts: Starting my math homework.

خب حالا callback خودمون رو می نویسیم، می توانیم آن را به عنوان آرگومان آخر تابع doHomeWork خودمون پاس دهیم. دقت کنید که در این مثال هم زمان هم تعریف کرده تابع را و هم به عنوان آرگومان ارسال می کنیم.

function doHomework(subject, callback) {
  alert(`Starting my ${subject} homework.`);
  callback();
}

doHomework('math', function() {
  alert('Finished my homework');
});

بعد از اجرای قطعه کد بالا می بینیدکه شما دو alert برای شروع و پایان homework دریافت می کنید. اما اجباری نیست که فانکش callback خودمون رو حتما در تعریف تابع اصلی تعریف کرد. میتوان در جای دیگری از کد فانکشن را تعریف و سپس به صورت آرگومان به فانکش اصلی فرستاد.

function doHomework(subject, callback) {
  alert(`Starting my ${subject} homework.`);
  callback();
}

function alertFinished(){
  alert('Finished my homework');
}

doHomework('math', alertFinished);

نتیجه اجرای قطعه کد بالا همانند مثال قبلی است فقط در تعریف و صدا زدن تفاوت دارند.

یک مثال دنیای واقعی

مثلا هنگام استفاده از API شرکت ها و سایت های دیگر شما به API آنها درخواست ارسال می کنید تا داده ای را بگیرید و بر اساس اون داده ادامه بدهید، پس نیاز دارید تا آماده شدن جواب منتظر بمانید. مثلا استفاده از API وب سایت توییتر

T.get('search/tweets', params, function(err, data, response) {
  if(!err){
    // This is where the magic will happen
  } else {
    console.log(err);
  }
});

فانکشن ()T.get برای ارسال درخواست GET به API به کار می رود.

تو این درخواست ما سه تا پارامتر وجود داره. اولی " search/tweets" که آدرس route ماست. Params پارامترهای ارسالی را میفرستیم باهاش و بعد از اون تابع callback ما

اینجا استفاده از callback خیلی مهم و کاربردیه چون ما برای ادامه کار نیاز داریم تا منتظر دریافت پاسخ از سرور بمونیم.

ما نمیدونیم که درخواست ما با با موفقیت ارسال میشه یا نه، باید منتظر جواب بمونیم. وقتی که توییتر جواب میده فانکشن callback ما فراخوانی یا اصطلاحا invoke میشه. توییتر یا یک آبجت err برمیگرداند به بخاطر fail شدن درخواست یا در صورت موفقیت آمیز بودن یک آبجکت response به عنوان جواب.

فانکشن های callback پر استفاده ترین تکنیک برنامه نویسی تابعی(functional) است. تقریبا در هرقطعه کد جاوا اسکریپت آنها رو می بینید.

خب تبریک شما تقریبا با مفهوم کلی و نحوه تعریف و استفاده از callback ها آشنا شدید.