30 روز با Node - روز هجدهم: توابع کال بک (Callback) در نود جی اس

03 شهریور 1397
callback-funtions-nodejs

کال بک ها (Callback) چیستند؟

نود جی اس ناهمزمان است و این بدان معنی است که در زمان اجرای وظایف و توابع خود، منتظر به پایان رسیدن بخش مسدود کننده (Blocking) نمی شود. این وظایف می تواند همانند عملیات ورودی/خروجی فایل، فراخوانی برخی RestAPI و انتظار برای نتیجه یا نوشتن داده ها در دیتابیس و غیره باشد. بلکه به جای انتظار، به سراغ وظیفه بعدی رفته و از کال بک ها استفاده کرده و به اجرای باقی دستورات و قسمتها ادامه می دهد. یک کال بک بطور ساده یک معادل غیرهمزمان برای یک تابع است که پس از اجرای یک وظیفه فراخوانی می شود. مفهوم کال بک باعث می شود اجرای برنامه هیچگاه بخاطر انتظار عملیاتی خاص، بلاک نشود و اجازه می دهد بقیه وظایف به کار خود ادامه دهند. دلیل نامگذاری توابع کال بک هم به این دلیل است که در برخی مقاطع زمانی، کال بک (Callback) خواهند شد (مثل زمانی که فردی به شما تماس می گیرد و شما نمی توانید جواب دهید و بعدا با وی تماس می گیرید). نود جی اس بطور وسیع و بی شمار از کال بک ها بهره می برد و تمام APIها در نود جی اس مفهوم کال بک را بخوبی پشتیبانی می کنند.

کدهای Non-Blocking در مقابسه با Blocking

یکی از ویژگیهایی که وجه تمایز نود جی اس است، این است که از کدنویسی non-blocking استفاده می کند. اجازه دهید این موضوع را با نگاهی به تفاوتهای کدنویسی Blocking و Non-Blocking خدمت شما عزیزان ارائه دهیم:

  1. فراخوانی برخی API های Rest و انتظار برای برگشت نتیجه از آنها
  2. نوشتن داده هایی در دیتابیس
  3. خواندن داده ها از یک فایل روی فایل سیستم
  4. نوشتن داده ها در یک فایل روی فایل سیستم

به تصویر زیر دقت کنید. تفاوت سناریو های non-blocking و blocking و چگونگی اجرای هر کدام در زمان خواندن یک فایل و چاپ محتوای آن چگونه خواهد بود:

اجرای توابع Callback‌در نود جی اس

توضیحات بیشتر همراه با قطعه کدهای داده شده در زیر آمده است:

Blocking code: تابعی که اجرای آن تحت تاثیر توابع دیگر با وظایف دیگر است یا به عبارت ساده، توابعی که همزمانی طبیعی دارند و دستورهای آنها به نوبت اجرا می شود، تحت blocking code درمی آیند.

//Name of the File is  : blocking-code.js
var fs = require('fs');

//For calculating execution time
var date1 = new Date();
var time_start = date1.getTime();
console.log("starting at: " + time_start);
console.log("Let's start reading file");


//Name of the file to be read
var filename = 'output.txt'; 
//Reading file synchronously
var content = fs.readFileSync(filename);
console.log('Content : ' + content);


//For calculating execution time
var date2 = new Date();
var time_end = date2.getTime();
console.log("finishing at: " + time_end);
var execution_time = time_end - time_start;
console.log("Time for execution: " + execution_time );


//Consider it some another task in queue
console.log('Another task to be executed');

که اجرا و نتیجه اجرای آن به صورت زیر است:

>node blocking-code.js
starting at: 1510699688357
Let's start reading file
Content : Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. A
enean massa. Cum sociis natoque pena....
...
...Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi. Curabitur
 ligula sapien, tincidunt non, euismod vitae, posuere imperdiet, leo. Maecenas malesuada. Praesent cong
ue erat at massa. Sed cursus turpis vitae tortor. Donec posuere vulputate arcu. Phasellus accumsan curs
us velit. Vestibulum ante ipsum primis in
finishing at: 1510699688374
Time for execution: 17
Another task to be executed

همانطور که می بینید، وظایفی منتظر می مانند تا عملیات خواندن فایل به پایان برسد و سپس اجرا می شوند. به همین دلیل این کد از نوع blocking است.

Non-Blocking Code: تابعی که اجرای آن تحت تاثیر بقیه توابع و وظایف نیست. به عبارت ساده تر، تابعی که طبیعت ناهمزمان دارد در این دسته بندی قرار میگیرد.

//Name of the file: non-blocking-code.js
var fs = require('fs');


//For calculating execution time
var date1 = new Date();
var time_start = date1.getTime();
console.log("{Check point 1} starting at: " + time_start);
console.log("Let's start reading file");


//Name of the file to be read
var filename = 'output.txt'; 
//Reading file asynchronously
fs.readFile('output.txt', (err, data) => {
	if (err) 
		throw err;
		
	console.log("Content :  " + data);
});


//For calculating execution time
var date2 = new Date();
var time_end = date2.getTime();
console.log("{Check point 2} finishing at:  " + time_end);
var execution_time = time_end - time_start;
console.log("Time for execution: " + execution_time );
//Consider it some another task in queue
console.log('Another task to be executed');

اجرا و نتیجه اجرای قطعه کد بالا را ببینید:

>node non-blocking-code.js
{Check point 1} starting at: 1510700249917
Let's start reading file
{Check point 2} finishing at: 1510700249920
Time for execution: 3
Another task to be executed
Content :  Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor.
Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Don
ec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Done
c pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a,
 venenatis vitae, justo.....
...
...Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi. Curabitur
 ligula sapien, tincidunt non, euismod vitae, posuere imperdiet, leo. Maecenas malesuada. Praesent cong
ue erat at massa. Sed cursus turpis vitae tortor. Donec posuere vulputate arcu. Phasellus accumsan curs
us velit. Vestibulum ante ipsum primis in

دو نکته مهم قابل ملاحظه در خروجی بالا وجود دارد:

  • وظیفه دیگری که باید اجرا شود قبل از اتمام عملیات خواندن فایل اجرا می شود و این همان طبیعت non-blocking تابع  و نیز مفهوم کال بک است.
  • نکته جالبی که باید ملاحظه نمود، این است که زمان اجرا فقط 3 است که نادرست است، زیرا برنامه بصورت ناهمزمان درحال اجراست و این عدد اختلاف زمان شروع و پایان برنامه نیست، بلکه شامل زمان اجرای دو دستوری است که زودتر از عملیات خواندن فایل اجرا و به پایان رسیده اند.

جهنم کال بک (Callback) در نود جی اس

کال بک ها اگر دارای سطح فراخوانی تودرتوی بالا باشند، باعث پیچیدگی می شوند و درک و عیب یابی آنها مشکل خواهد شد. جهنم کال بک گاهی به هرم مرگ هم شناخته می شود.

fxn1(function(){
    fxn2(function(){
        fxn3(function(){
            fxn4(function(){
				fxn5(function(){
					fxn6(function(){
						fxn7(function(){
							fxn8(function(){
								....
            
			
							});
						});
					});
				});
			});
        });
    });
});

پرهیز از جهنم کال بک

  1. ماژولار کردن: برای پرهیز جهنم کال بک از تا جایی که امکان دارد، برنامه را ماژولار کنید.
  2. استفاده از Promiseها:  از مفهوم Promiseها برای اجتناب از جهنم کال بک می توانید استفاده کنید.
  3. Async.js: یک ماژول بسیار قدرتمند در مدیر پکیج نود (npm) که می تواند برای پرهیز از جهنم کال بک مفید واقع شود.
  4. زنجیره Promiseها: این زنجیره برای جلوگیری از جهنم کال بک بسیار مفید است.

خلاصه

در درس امروز از سلسله دروس 30 روز با نود جی اس، با مفهوم کال بک آشنا شدیم. تفاوت کد blocking و non-blocking را متوحه شدیم و در نهایت راههایی برای فرار از جهنم کال بک بیان کردیم.

نویسنده شوید
دیدگاه‌های شما

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