Node.js یک محیط سمت سرور برای اجرای کدهای JavaScript است؛ اما سؤال اینجا است که این گزاره به چه معنا است و اساساً چنین محیط اجرایی چگونه کار میکند؟ برای کسب اطلاعات بیشتر در مورد این فناوری، یافتن پاسخ به سؤالات فوق و همچنین بسیاری سؤال مرتبط دیگر، در این مقاله با سکان آکادمی همراه باشید.
Node Run-time Environment (محیط اجرای نُود) همه قابلیتهایی را شامل میشود که یک دولوپر جاوااسکریپت برای اجرای اسکریپتهای خود نَه در سمت فرانتاند، بلکه در سمت بکاند (سرور) به آنها نیارمند است. در حقیقت، Node.js در نتیجهٔ تلاشهای دولوپرهای زبان جاوااسکریپت برای توسعۀ محیطی به وجود آمد که دولوپرها بتوانند کدهای جاوااسکریپت خود را علاوه بر داخل مرورگر، در سمت سرور نیز در قالب یک اپلیکیشن مستقل اجرا کنند.
در واقع، با وجود چنین قابلیتی دولوپرها میتوانند علاوه بر طراحی وبسایتهای تعاملی با زبان #جاوااسکریپت، برای توسعهٔ سایر اپلیکیشنها نیز از این زبان استفاده کنند به طوری که پس از ظهور نودجیاس، کاربردهای این زبان به اندازهٔ سایر زبانهای اسکریپتی همچون پایتون افزایش یافته است.
همچنین هر دو محیط اجرای جاوااسکریپت (هم مرورگر و هم محیط سمت سرور) روی موتور جاوااسکریپت تحت عنوان V8 اجرا میشوند. در حقیقت، این موتور کدهای جاوااسکریپت را گرفته و آنها را به یک اسکریپتی قابلفهم برای ماشین تبدیل میکند (کد قابلفهم برای ماشین یک کد اصطلاحاً Low Level یا سطح پایین است که کامپیوتر میتواند بدون تفسیر، خیلی سریع آن را اجرا کند.)
Node.js چیست؟
بر اساس تعریف ارائهشده در وبسایت رسمی Node.js:
نودجیاس یک محیط اجرای جاوااسکریپتی است که روی موتور اجرای کد جاوااسکریپتِ مرورگر کروم تحت عنوان V8 ساخته شده است.
V8 یک موتور اپنسورس برای اجرای کدهای جاوااسکریپت است که با زبان ++C نوشته شده است و همانطور که پیش از این نیز ذکر شد، این موتور کدهای جاوااسکریپتی را میگیرد و به یک کد قابلفهم برای ماشین تبدیل میکند. در واقع، V8 همچون توربینی با بهکارگیری زبان ++C موجب افزایش کارایی در خروجی کد جاوااسکریپت میشود.
همچنین V8 استانداردی تحت عنوان ECMAScript را پیادهسازی میکند که توسط سازمان بینالمللی Ecma و به منظور استانداردسازی زبان جاوااسکریپت ساخته شد. این موتور توانایی اجرایی کدها به صورت مستقل را دارا است و میتواند در سایر اپلیکیشنهای نوشته شده با ++C نیز اِمبدد شود. به عبارت دیگر، دولوپرها میتوانند کد خود را به زبان ++C بنویسند و این در حالی است که امکان اجرای آن در محیط اجرای کدهای جاوااسکریپت و همچنین نوشتن کد به زبان جاوااسکریپت نیز برای ایشان فراهم است که این مسئله موجب میشود تا دولوپرها با اِمبدد کردن موتور V8 در کد نوشته شده با زبان سیپلاسپلاس خود، فیچرهایی را به زبان جاوااسکریپت بیفزایند تا این کد قابلیتهای بیشتری نسبت به سایر استانداردهای مشخصشده در ECMAScript را داشته باشد.
به غیر از تعریف فوق، تعاریف و تفاسیر دیگری نیز برای نودجیاس ارائه شدهاند که در ادامه به معرفی تعریف جامعتری از این محیط میپردازیم:
نودجیاس از مُدلی پیروی میکند که مبتنی بر رویداد (Event-driven) بوده و همچنین این مدل فرآیندهای ورودی و خروجی (I/O) را اصلاً بلاک نمیکند به طوری که استفاده از چنین مدلی موجب سَبکی و کارآمدی محیط اجرای نودجیاس شده است.
تعریف اول از نودجیاس را در ابتدای مقاله مورد بحث قرار دادیم؛ حال به بررسی تعریف دوم خواهیم پرداخت تا دریابیم که چرا نودجیاس محبوب شده است.
درآمدی بر Blocking I/O و Non-blocking I/O
I/O به درخواستهای ورودی و خروجی از یک سیستم اشاره دارد و فرآیندهای گوناگونی را شامل میشود که از آن جمله میتوان به فرآیندهای به اصطلاح Read یا Write (به ترتیب به معنی خواندن و نوشتن) روی یکسری فایل سیستمی یا ارسال یک ریکوئست (درخواست) از نوع HTTP به یک API را نام برد. معمولاً چنین ریکوئستهایی زمانبَر هستند، لذا سیستم در هنگام دریافت درخواستهایی از جنس I/O، فانکشنهای دیگر را بلاک (مسدود) میکند تا بتواند در کمترین زمان ممکن پاسخ مناسب را به این درخواستها بدهد. برای درک بهتر این موضوع، سناریوی فرضی زیر را در نظر بگیرید:
یک درخواست از طرف سیستم به دیتابیس ارسال شده است تا اطلاعات کاربر شماره یک و همچنین کاربر شماره دو را دریافت کرده و آنها را در یک صفحه یا در کنسول چاپ کند. پاسخ به این ریکوئست کمی زمانبَر است اما هر دو درخواست برای چاپ دیتای کاربران میتوانند به صورت مستقل از هم و همزمان انجام شوند.
حال فرض کنید که ارسال ریکوئست و دریافت ریسپانس (پاسخ) از دیتابیس بر طبق فرآیند Blocking I/O (مسدود کردن ورودی/خروجی) انجام میشد؛ در این روش پاسخ به ریکوئستی که برای دریافت دیتای کاربر دوم ارسال شده داده نمیشود مگر زمانی که کار ریکوئست اول (دریافت دیتای کاربر قبلی) به اتمام رسیده باشد که این اصلاً خوب نیست!
اگر چنین ریکوئستی به یک وبسرور ارسال شود، بایستی به ازای هر ریکوئست برای دریافت دیتای مربوط به هر کاربر، یک به اصطلاح Thread جدید ایجاد شود اما زبان جاوااسکریپت یک زبان به اصطلاح Single-threaded (تَک تِرِدی) است؛ بنابراین برای تَسکهایی که درخواست به یک وب سرور ارسال میشود و نیاز به اجرا به صورت به اصطلاح Multi-threaded (چند تِرِدی) دارند، زیاد مناسب نخواهد بود (لازم به ذکر است که زبان جاوااسکریپت کاملاً تکتِردی نبوده اما دارای یک Event Loop است که به صورت تَک تِرِدی اجرا میشود که در ادامۀ مقاله، این مورد را بیشتر توضیح میدهیم.)
با در نظر گرفتن این شرایط، سؤالی که پیش میآید این است که در زبان جاوااسکریپت درخواستهای همزمان چگونه اجرا میشوند؟ در پاسخ به این سؤال فرآیندی را معرفی خواهیم کرد که به روش Non-blocking I/O (مسدود نکردن درخواستهای ورودی/خروجی) اجرا شده و برای انجام درخواستهای همزمان بسیار کارآمد است.
برای مثال، با بهکارگیری فرآیندهایی که در آن یکی از چند درخواست همزمان بلاک نمیشوند، سیستم میتواند یک ریکوئست را برای دریافت دیتای مربوط به کاربر شماره دو آغاز کند، بدون اینکه منتظر دریافت پاسخ مربوط به دیتای کاربر شماره یک بماند. در واقع، سیستم هر دو درخواست را به صورت موازی اجرا میکند و در کوتاهترین زمان ممکن پاسخ را به کاربران ارسال میکند که در این صورت دیگر نیازی به اجرای تَسکها به صورت چند تِردی نیست چرا که سرور میتواند چندین درخواست را به صورت همزمان هَندل کند.
درآمدی بر Event Loop در جاوااسکریپت
هر آنچه در اپلیکیشن اتفاق میافتد و دولوپر میتواند به آن پاسخ دهد را اصطلاحاً Event میگویند. به طور کلی دو نوع ایونت (رویداد) در پلتفرم نودجیاس وجود دارد که عبارتند از:
- ایونتهای سیستمی: اینگونه ایونتها در هستهٔ ++C و در نتیجۀ فراخوانی یک لایبرری تحت عنوان libuv اتفاق میافتد (به عنوان مثال، میتوان به پایان رسیدن فرآیند Read یک فایل را مثال زد.)
- ایونتهای سفارشی شده: این دست رویدادها در هستۀ جاوااسکریپت اتفاق میافتند.
حال پس از آشنایی با مفهوم Event (رویداد)، در ادامه قصد داریم به تشریح گامبهگام نحوۀ اجرای Event Loop (حلقهای از رویدادها) در جاوااسکریپت بپردازیم.
همانطور که در تصویر فوق مشخص است، ابتدا تابعی تحت عنوان ()main وارد Call Stack (پشتهای به منظور فراخوانی و اجرای توابع) میشود و در ادامه دستور ()console.log وارد Call Stack شده و فوراً اجرا میشود و از پشته نیز خارج میشود. در این مرحله، تابع (setTimeout(2000 وارد پشته میشود (تابع (setTimeout(2000 یک ایپیآی برای نودجیاس است و وقتی آن را فراخوانی میکنیم یک جفت Event-Callback را رجیستر میکنیم که در آن ایونتی به مدت 2000 میلیثانیه منتظر مانده و سپس مجدداً تابع Callback فراخوانی میشود.) پس از رجیستر کردن جفت Event-Callback، تابع (setTimeout(2000 از اِستک (پُشته) خارج میشود.
در مرحلهٔ بعد، تابع (setTimeout(0 به همین شیوه رجیستر میشود؛ حال دو ایپیآی Node داریم که منتظر اجرا هستند. تابع (setTimeout(0 بدون منتظر ماندن به Callback Queue (صف فراخوانی مجدد تابع) منتقل میشود و پس از 2000 ثانیه نیز تابع (setTimeout(2000 به صف Callback منتقل میشود. در صف Callback، این تابع صبر میکند تا پشته فراخوانی تابع خالی شود، زیرا تنها یک دستور میتواند در یک زمان اجرا شود و این در حالی است که فراخوانی توابع برای اجرا توسط Event Loop هندل میشود و در نهایت هم دستور ()console.log اجرا میشود و تابع ()main از پشته فراخوانی توابع خارج میشود.
در آنچه توضیح دادیم، Event Loop، که وظیفۀ هندل کردن توابع را بر عهده داشت، میبیند که پشته توابع خالی شده است اما صف Callback خالی نیست؛ بنابراین این حلقه توابعی را که برای اجرای مجدد فراخوانی شدهاند، بر اساس قانون FIFO از این پشته خارج کرده و وارد Call Stack میکند تا به ترتیب اجرا شوند (FIFO مخفف واژگان First In, First Out است و در ساختمان داده بدان معنا است که آنچه در ابتدا وارد پشته شود، ابتدا نیز خارج یا اجرا میگردد.)
درآمدی بر پَکیج مَنجر NPM
NPM که مخفف واژگان Node Package Manager است، پَکیج مَنجر نودجیاس حاوی مجموعهای از لایبرریهایی است که با مشارکت کامیونیتی بزرگی از دولوپرهای جاوااسکریپت توسعه یافته است و پاسخی به نیازهای بسیاری از مسائل دولوپرها است.
آشنایی با ماژولهای پلتفرم Node.js
یک ماژول در نُود بلوکی از کد با قابلیت استفادهٔ مجدد است و اجرای این بلوک از کد بر روی کدهای دیگر تأثیر نمیگذارد. همچنین دولوپرها میتواند ماژولهای خود را نوشته و از آنها در برنامههای مختلف استفاده کنند و این در حالی است که خودِ پلتفرم نودجیاس نیز متشکل از مجموعۀ ماژولهای مختلفی است که دولوپرها میتوانند بدون نصب، آنها را مورد استفاده قرار دهند.
آشنایی با نحوۀ نوشتن Hello World در Node.js
برای این منظور، ابتدا یک فایل با نامی دلخواه همچون app.js ساخته و کد زیر را داخل آن مینویسیم:
console.log("Hello World!");
ترمینالِ سیستمعاملی که نُود رویش نصب است را باز کرده و دایرکتوری آن را به مسیری که فایل app.js در آن ذخیره شده، تغییر دهید. حال دستور زیر را اجرا کنید:
$ node app.js
بدین ترتیب، برنامهٔ سادهٔ Hello World در محیط نودیجیاس اجرا میشود!
درآمدی بر MEAN Stack
به طور کلی، هر زبان برنامهنویسی دارای فناوریهای وابسته به خود به منظور توسعهٔ سریعتر اپلیکیشنها است که از آن جمله میتوان به پکیجها، لایبرریها، فریمورکها و ... برای هر زبان و محیط توسعهای اشاره کرد و این در حالی است که Node.js هم از این قاعده مستثنی نیست.
MEAN سرواژهای از حرف اول نام فناوریهای AngularJS، Express، MongoBD و NodeJS است؛ دقیقاً چیزی همچون اِستک LAMP که پکیجی از محیط توسعه (Linux)، وبسرور (Apache)، دیتابیس (MySQL) و زبان برنامهنویسی (PHP) است. در توضیح اِستک MEAN به نظر میرسد که به اندازهٔ کافی در مورد NodeJS تاکنون صحبت کردهایم و بهتر است بپردازیم به سه مورد دیگر که عبارتند از:
آشنایی با فریمورک Express
Express یک فریمورک سَبک است که برای توسعهٔ وب اپلیکیشن با استفاده از نُود به کار میرود. به عنوان نمونه سورسکد نوشته شده با این فریمورک داریم:
const express = require('express')
const app = express()
app.get('/', (req, res) => res.send('Hello World!'))
app.listen(3000, () => console.log('Example app listening on port 3000!'))
آشنایی با فریمورک AngularJS
AngularJS یک فریمورک جاوااسکریپتی است که توسط کمپانی گوگل عرضه شده است که فیچرهای قابلتوجهی منجمله Two-way Data Binding در اختیار دولوپرها میگذارد و به نوعی میتوان گفت که سولوشن خوبی برای توسعهٔ فرانتاند است. به عنوان نمونه سورسکد نوشته شده با این فریمورک داریم:
angular.module('todoApp', [])
.controller('TodoListController', function() {
var todoList = this;
todoList.todos = [
{text:'learn AngularJS', done:true},
{text:'build an AngularJS app', done:false}];
todoList.addTodo = function() {
todoList.todos.push({text:todoList.todoText, done:false});
todoList.todoText = '';
};
todoList.remaining = function() {
var count = 0;
angular.forEach(todoList.todos, function(todo) {
count += todo.done ? 0 : 1;
});
return count;
};
todoList.archive = function() {
var oldTodos = todoList.todos;
todoList.todos = [];
angular.forEach(oldTodos, function(todo) {
if (!todo.done) todoList.todos.push(todo);
});
};
});
آشنایی با دیتابیس MongoDB
MongoDB یک دیتابیس از نوع NoSQL است که دیتا را به صورت فرمت جیسون ذخیره میسازد (در همین راستا، میتوانید به مقالهٔ درآمدی بر انواع مختلف دیتابیسهای NoSQL مراجعه نمایید.) به عنوان نمونه سورسکد نوشته شده با نودجیاس برای ارتباط با این دیتابیس داریم:
const MongoClient = require('mongodb').MongoClient;
const assert = require('assert');
// Connection URL
const url = 'mongodb://localhost:27017';
// Database Name
const dbName = 'myproject';
// Use connect method to connect to the server
MongoClient.connect(url, function(err, client) {
assert.equal(null, err);
console.log("Connected successfully to server");
const db = client.db(dbName);
client.close();
});
در پایان، اگر علاقمند به فناوری Node.js هستید، میتوانید به تگ #نودجیاس در وبلاگ سکان آکادمی مراجعه نمایید که مقالاتی مرتبط با این فناوری را شامل میگردد که برخی از مهمترین آنها عبارتند از:
- مقایسهای مابین Node.js و Go
- NodeOS: سیستمعاملی کم حجم بر پایهٔ Node.js
- Total.js: فریمورکی برای نودجیاس
- Strapi: فریمورک اپنسورس مبتنی بر Node.js برای ساخت RESTful API
- Relax: نسل جدید CMS بر پایهٔ React و Node.js
- Moleculer: فریمورک میکروسرویس سریع، مدرن و قدرتمند برای Node.js
حال نوبت به نظرات شما میرسد. آیا تاکنون تجربهٔ کدنویسی در محیط Node.js را داشتهاید و در مقایسه با دیگر زبانهای سمت سرور همچون پایتون، گو و غیره، به نظر شما چه آیندهای در انتظار این فناوری است؟ نظرات، دیدگاهها و تجربیات خود را با دیگر کاربران سکان آکادمی به اشتراک بگذارید.