آموزش NodeJS قسمت اول

آموزش NodeJS قسمت اول

درک معماری Node.js

برگردیم به روزهایی که رایان علاقه مند به توسعه برنامه کاربردی شبکه بود. او به این نتیجه رسید که بیشتر سرورها با کارایی بالا، مفاهیم مشابه‌ای دارند. معماری آنها شبیه به یک رویداد حلقه است و با عملیات ورودی / خروجی nonblocking کار می‌کنند. این عملیات به سایر

پردازش‌ها این اجازه را می‌دهد تا قبل از اینکه یک پردازش به اتمام برسد به کار خود ادامه دهند. این ویژگی بسیار مهم است اگر می‌خواهیم به هزاران درخواست بطور همزمان رسیدگی کنیم.

بیشتر سرورها در Java و C نوشته شده اند که از چند ریسمانی استفاده می کنند. بطور دقیقتر یعنی آنها برای هر درخواست جدید که صورت می گیرد یک ریسمان تعریف می کنند. رایان تصمیم گرفت که یک چیز متفاوت را امتحان کند – معماری تک ریسمانی. به عبارت دیگر، تمام

درخواست هایی که به سرور می شود تنها با یک ریسمان واحد پردازش می شود. شاید این روش مقیاس پذیر به نظر نیاید، اما Node.js قطعا مقیاس پذیر می باشد.

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

داده‌اند. آنها در گذر زمان سریع و سریعتر شده اند. معماری event-loop آنها پیاده سازی شد. جاوا اسکریپت در سال‌های اخیر بسیار محبوب شده است. کامنیوتی و صدها هزار توسعه دهنده آماده کمک و توسعه ایده رایان با استفاده از جاوا اسکریپت شدند.

بطور کلی، نود.جی اس از سه چیز تشکلیل شده است:

V8 موتور جاوا اسکریپت گوگل است که مرورگر کروم استفاده می شود

(https://developers.google.com/v8/)

تنها یک ریسمان یا thread تمام عملیات ورودی و خروجی فایل را به عهده گرفته است.

کتابخانه event loop

(http://software.schmorp.de/pkg/libev.html)

نصب Node.js

روش سریع و آسان نصب نود.جی اس مراجعه به سایت https://nodejs.org/download دانلود و نصب آن متناسب با سیستم عامل خود است. برای کاربران OS X و Windows نصب بصورت آسان با یک رابط کاربری ساده می‌باشد. برای آن دسته از توسعه دهندگانی هم که

از Linux استفاده می‌کنند، Node.js با استفاده از مدیریت بسته‌های خود APT قابل دسترس می‌باشد. دستورات زیر Node.js را همراه با پکیج منیجر آن یعنی NPM نصب می‌کند.

Sudo apt-get update Sudo apt-get install nodejs Sudo apt-get install npm

راه اندازی سرور Node.js

Node.js یک ابزار خط فرمان است. بعد از نصب آن، با اجرای دستور node در ترمینال قابل دسترس خواهد بود. دستور node چندین آرگومان را قبول می کند، اما مهمترین آنها فایلی است که شامل جاوا اسکریپت ما می باشد. اجازه دهید یک فایل با نام server.js ایجاد کرده

و کدهای زیر را به آن اضافه کنیم:

var http = require('http'); http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World\n'); }).listen(3000, '127.0.0.1'); console.log('Server running at http://127.0.0.1:3000/');

اگر شما دستور node ./server.js در خط فرمان خود اجرا کنید خواهید دید که سرور Node.js اجرا می شود. این سرور به درخواست هایی که در آدرس localhost با پورت 3000 به آن می شود پاسخ می دهد. در خط اول از کد فوق، نیاز به استفاده از ماژول http که از پیش

در Node قرار داده شده می باشد. در Node ما تابعی سراسری با نام require داریم که مکانیسمی را فراهم می کند تا از ماژول های خارجی استفاده کنیم. در ادامه خواهید دید که چطور ماژول های خود را در برنامه تعریف کنیم. بعد از آن اسکریپت به اجرای خود ادامه می دهد تا

به متد createServer و listen از ماژول http برسد. در این مورد API ماژول بصورتی طراحی شده است که می توانیم این دو متد را مثل jQuery به صورت زنجیروار به مهم متصل کنیم.

اولین متد ( createServer ) تابعی را به عنوان پارامتر قبول می کند که به عنوان callback شناخته شده است که با هر بار درخواست جدید به سرور این تابع فراخوانی می شود. دومین متد (listen) باعث گوش فرا دادن به سرور می شود.

تعریف و استفاده از ماژول ها

JavaScript به عنوان یک زبان، مکانیزم هایی ب رای تعریف کلاس های واقعی ندارد. در واقع، هر چیزی در جاوا اسکریپت یک شیء می باشد. ما بطور معمول خواص و توابع را از یک شیء به شیء دیگر به ارث می بریم. خوشبختانه، Node مفاهیم تعریف شده توسط CommonJS را

اتخاذ کرده است ، پروژه ای است که یک اکوسیستم برای جاوا اسکریپت مشخص می کند.

هر ماژول در فایل مخصوص به خود تعریف می شود و منطق ماژول ها را کپسوله سازی می کنیم.

اجازه دهید با نمایش دادن یک مثال ساده نشان دهم چطور کار می کند. بیایید فرض کنیم یک ماژولی داریم که این کتاب را نشان می دهد و ما آن را در یک فایل با نام book.js ذخیره کرده ایم:

// book.js exports.name = 'Node.js by example'; exports.read = function() { console.log('I am reading ' + exports.name); };

همانطور که مشخص است ما یک مشخصه و تابع عمومی تعریف کرده ایم. حالا می توانیم با استفاده از تابع require به آنها دسترسی داشته باشیم:

// script.js var book = require('./book'); console.log('Name: ' + book.name); book.read();

کد فوق را در یک فایل جدید با نام script.js ذخیره کرده ایم، حال می توانیم برنامه را با اجرا دستور node./script.js اجرا کنیم. نتیجه در ترمینال بصورت زیر خواهد بود:

Lucy@LUCY-PC /c/Users/Lucy/Desktop/book $ node script.js Name: node.js by example I am reading node.js by example

در کنار exports ما همچنین module.exports نیز در دسترس است. یک تفاوت بین این دو وجود دارد. به شبه کد زیر توجه کنید. نشان می دهد که چطور Node.js ماژول های ما را بنا می کند:

<p>var module = { exports: {} }; var exports = module.exports; // our code return module.exports;</p>

بنابراین هماطور که مشخص است در خط آخر module.exports برگشته داده می شود و این چیزی است که در require می بینیم. ما باید مراقب باشیم چون گاهی اوقات ما مقادیری را بطور مستقیم به exports و module.exports اختصاص می دهیم که ممکن است آن چیزی که نیاز داریم را دریافت نکنیم. درست مثل تکه کد زیر، ما یک تابع را به عنوان یک مقدار تعریف کرده ایم :

exports.name = 'Node.js by example'; exports.read = function() { console.log('Iam reading ' + exports.name); } module.exports = function() { ... }

در این مورد ما به مشخصه .name و متد .read دسترسی نداریم. اگر سعی داشته باشیم node ./script.js را دوباره اجرا کنیم، با خروجی زیر روبرو می شویم:

Book.read(); ^ TypeError: undefined is not a function

برای جلوگیری از چنین مسائلی، باید به یکی از این دو مورد بچسبیم – exports یا module.exports – اما مطمئن شوید که از هر دوی آنها استفاده نمی‌کنید.

ما همچنین باید در ذهن داشته باشیم که بطور پیشفرض، require شیء‌های برگشت (return) داده شده را کش (cache) می‌کند. بنابراین اگر به دو نمونه مختلف نیاز داشته باشیم، باید تابع را export کنیم. در زیر نسخه ای از کلاس book را مشاهده می‌کنید که API‌هایی را برای رای دادن به کتاب ها فراهم می کند

که به درستی کار نخواهد کرد:

var ratePoints = 0; exports.rate = function(points) { ratePoints = points; }; exports.getPoints = function() { return ratePoints; };

بیایید دو نمونه از را ایجاد کنیم و به کتاب ها با مقداری مختلف (points)رای دهیم:

var bookA = require('./book'); var bookB = require('./book'); bookA.rate(10); bookB.rate(20); console.log(bookA.getPoints(), bookB.getPoints());

اگر برنامه را اجرا کنید، منطقا باید 10 20 در خروجی نشان داده شود، ولی اینطور نیست و 20 20 نشان داده می شود. در زیر یک راه حل مرسوم وجود دارد که یک تابع را export می کند که وظیفه آن این است که در هر بار یک شیءمتفاوت تولید کند:

module.exports = function() { var ratePoints = 0; return { rate: function(points) { ratePoints = points; }, getPoints: function() { return ratePoints; } } };

حال نیاز است که فایل script.js را بصورت زیر تغییر دهیم:

var bookA = require('./book')(); var bookB = require('./book')(); bookA.rate(10); bookB.rate(20); console.log(bookA.getPoints(), bookB.getPoints());

چون require یک تابع برگشت می‌دهد و نه یک شیء.

مدیریت و توزیع بسته ها

حال که مفهوم require و exports را درک کردیم، زمان آن است شروع به فکر کردن در مورد بلوک‌های ساختمانی شویم. در دنیای Node.js این بلوک‌ها را module یا package می‌نامند. یکی از دلایل محبوبیت Node.js مدیریت پکیج آن است.

Node.js بطور معمول با دو چیز اجرایی منتشر می‌شود — node و npm . NPM یک ابزار خط فرمان است که بسته‌های Node.js را دانلود و آپلود می‌کند. سایت رسمی tps://npmjs.org/ به عنوان یک رجیستری مرکزی عمل می‌کند. هنگامی که از طریق دستور npm پکیجی را

ایجاد می‌کنیم، آن را در داخل آن رجیستری ذخیره می‌کنیم، بطوری که هر توسعه دهنده دیگری ممکن است از آن استفاده کند.

ایجاد module

هر ماژول هم مثل هر پروژه ای دارای دایرکتوری مخصوص به خود می باشد، که همچنین شامل یک فایل متادیتا (package.json) است. ما در این فایل مجموعه ای با حداقل دو مشخصه داریم name: و version

{ "name": "my-first-nodejs-module", "version": "0.0.1" }

هنگامی که ماژول را در رجیستری NPM منتشر می‌کنیم و شخص دیگری آن را دانلود می‌کند، او نیز همان فایل‌ها را دریافت می‌کند. به عنوان مثال، اجازه دهید یک فایل جدید به نام index.js ایجاد کنیم، بطوری که حال دو فایل در پکیج داریم

// index.js console.log('Hello, this is my first Node.js module!');

ماژول ما تنها شامل یک چیز است — آن فقط یک پیام ساده را در کنسول نمایش می‌دهد. حال، برای اینکه ماژول را آپلود کنیم، نیاز داریم که به آدرس دایرکتوری خود که شامل فایل package.jsonاست مراجعه کنیم و دستور npm publish را اجرا کنیم.

Lucy@LUCY –PC /c/Users/Lucy/Desktop/my-first-module $ npm publish + my-first-nodejs-module@0.0.1

قبل از اینکه دستور بالا را اجرا کنید مطمئن شوید حساب کاربری در سایت npmjs.org ایجاد کرده‌اید و با دستور npm login مشخصات حساب کاربری خود را وارد کرده‌اید:

Lucy@LUCY –PC –PC /c/Users/Lucy/Desktop/my-first-module $ npm Login Username: Password: Email:(this is public ):

ما آماده‌ایم! حال ماژول کوچک ما در سایت پکیج منیجر Node قرار گرفته شده است و هر شخصی می‌تواند آن را دانلود کند.

استفاده از modul

بطور کلی، سه روش برای استفاده از ماژول ها در حال حاضر وجود دارد:

شاید نیاز باشد ماژول خاصی را نصب کنیم. بیایید فرض کنیم یک پوشه به نام project داریم، آن را باز می کنیم و دستور زیر را در آن اجرا می کنیم:

npm install my-first-nodejs-module

1.پکیج منیجر بطور خودکار آخرین نسخه از ماژول را دانلود کرده و در داخل دایکتوری به نام node_modules ذخیره می‌کند. همچنین اگر بخواهیم از ماژولنصب شده استفاده کنیم، نیازی نیست مسیر دقیق پکیج را مشخص کنیم. بطور پیشفرض Node.js پوشه node_modules

را بررسی می‌کند. بنابراین، تنها کافیست require(‘my-first-nodejs-module’) را تایپ کنیم.

2.نصب ماژول بصورت سراسری (global) بسیار رایج است، بخصوص اگر بحث درباره ساخت ابزارهای خط فرمان با Node.js باشد. این روش استفاده آسان از تکنولوژی را برای توسعه چنین ابزارهایی را میسر کرده است. این ماژول کوچکی که ماایجاد کرده ایم به عنوان یک برنامه

خط فرمان ساخته نشده است، اما با این حال می توانیم آن را بطور global با دستور زیر در سیستم نصب کنیم:

npm install –g my-first-nodejs-module

به –g توجه داشته باشید! این چیزیست که به منیجر می گوید ما می‌خواهیم ماژول خود را بصورت global نصب شود. هنگامی که پردازش به پایان رسید، مااین دفعه دیرکتوری node_modules را نخواهیم داشت. اگر ماژول ما نصب شده است

پس کجا ذخیره شده؟ خوب پوشه my-first-nodejs-module در یک جای دیگر از سیستم ما ذخیره شده است! برای اینکه قادر به استفاده از آن باشیم، نیاز است که مشخصه دیگری را به فایل package.json اضافه کنیم. اما درباره این موضوع در بخش بعدی صحبت خواهیم کرد.

3.حل و فصل وابستگی‌ها(dependencies) یکی از ویژگی‌های کلیدی است که مدیریت بسته Node.js به آن رسیدگی می‌کند. به عبارت ساده هر ماژول می‌تواند به چندین ماژول دیگر وابسته باشد. به عنوان مثال فرض کنید یک ماژول جدید می‌خواهیم درست کنیم که به ماژول

قبلی که دررجیستری منتشر کردیم وابسته است، تمام کاری که لازم است انجام دهیم این است که در پوشه جدید ماژول فایل package.json را تعریف کرده و json زیر را به آن اضافه کنید:

{ "name": "another-module", "version": "0.0.1", "dependencies": { "my-first-nodejs-module": "0.0.1" } }

در حال حاضر نیازی نیست که ماژول خود را به صراحت مشخص کنیم تنها کافیست دستور NPM install اجرا کنیم تا وابستگی ها نصب شوند. منیجر فایل package.json را خوانده و دوباره ماژول ما را در دایرکتوری node_modules ذخیره می کند.

این روش بسیار مفید است اگر بخواهیم چندین وابستگی (dependency) را تنها با یک دستور نصب کنیم. این روش همچنین ماژول ما را قابل انتقال می کند، یعنی کافیست در یک سیستمدیگر دستور npm install را اجراکنید و منتظر جادو باشید. البته نیازی نیست که به دیگر

توسعه دهنده ها توضیح دهید ماژول ما از چه چیزی ساخته شده است.

به روز رسانی ماژول

اجازه دهید ماژول خود را به یک ابزار خط فرمان تبدیل کنیم. وقتی این کار راانجام دهید، کاربران دیگر نیز یک دستور my-first-nodejs-module را در ترمینال خود دارند. دو تغییر در فایل package.json وجود دارد که باید به آن توجه شود:

{ "name": "my-first-nodejs-module", "version": "0.0.2", "main": "index.js" }

همانطور که مشاهده می‌کنید مشخصه جدید main را به فایل اضافه کرده‌ایم، اشاره به فایل اصلی برنامه ما می‌کند، به عبارت ساده اشاره به فایلی می‌کند که نقطه ورود به برنامه را مشخص می‌کند. در این مثال ساده ما تنها یک فایل index.js داریم.

تغییر دیگری که در فایل برای به روزرسانی پکیج ایجاد شده مشخصه version می‌باشد. در Node.js شماره نسخه ماژول نقش مهمی را ایفا می‌کند. هر شماره در مشخصهversion معنی خاصی دارد. پکیج منیجر از Semantic Versioning 2.0.0 )http://semver.org/) استفاده

می‌کند. فرمت آن MAJOR.MINOR.PATCH می‌باشد. بنابراین به عنوان توسعه دهنده باید به موارد زیرتوجه داشته باشیم:

•شماره MAJOR اگر تغییرات عمده‌ای در API انجام داده‌ایم

•شماره MINOR اگر ویژگی / توابع جدیدی را اضافه کرده‌ایم

•شماره PATCH اگر باگی را فیکس کرده‌ایم

گاهی اوقات ما نسخه‌هایی مثل 2.12.* را مشاهده می کنیم. به این معناست که توسعه دهنده علاقه مند به استفاده از نسخه MAJOR و MINOR دقیق است، اماموافقت می‌کند که در آینده باگ‌ها فیکس خواهند شد. همچنین ممکن است از مقادیری مثل >=1.2.7 یا مساوی یا

کوچکتراستفاده شود، به عنوان مثل 1.2.7 و 1.2.8 یا 2.5.3 ما فایل package.json را ویرایش کردیم! حال نوبت آن است که تغییرات را به رجیستری ارسال کنیم. این کار را دوباره با دستور npm publish در دایرکتوری که فایل JSON را نگهداری می‌کند انجام می‌دهیم. نتیجه

شبیه به زیر خواهد بود. ما شماره نسخه 0.0.2 را در صفحه مشاهده خواهیم کرد:

<p class='leftTxt'><xmp>Lucy@LUCY –PC –PC /c/Users/Lucy/Desktop/my-first-module $ npm publish + my-first-nodejs-module@0.0.2

درست بعد از این کار، دستور npm install –g my-first-nodejs-module را اجرا کنید تا نسخه جدید از ماژول شما در سیستم دانلود و نصب شود. تفاوت این است که حال دستور my-first-nodejs-module از طریق خط فرمان قابل دسترس است، اگر آن را اجرا کنید پیام

نوشته شده در فایل index.js نمایش داده می شود.

ساخت سرور با ماژول HTTP

ما در حال حاظر می‌خواهیم از ماژول HTTP استفاده کنیم. این شاید یکی از مهمترین بخش ها برای توسعه وب است که یک سرور را در پورت خاصی اجرا می‌کند:

var http = require('http'); http.createServer(function(req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World\n') }).listen(3000, '127.0.0.1'); console.log('Server running at http://127.0.0.1:3000/');

ما متد createServer را داریم که یک شیء جدید وب سرور به ما بر می گرداند. در بیشتر موارد ما از متد listen استفاده می کنیم. اگر لازم باشد از close نیز می شود استفاده کرد که باعث می شود سرور وقتی ارتباط جدیدی را دریافت کرد متوقف شود. تابع callback همیشه

اشیا request و response را قبول می کند. با استفاده از request اطلاعاتی که از درخواست ها می آید را بازیابی می کنیم، مثل پارمتر های GET و POST.

خواندن و نوشتن فایل ها

ماژولی که مسئول فرآیندهای خواندن و نوشتن است fs نامیده شده است. در زیر یک مثال ساده نمایش داده می‌شود که داده‌ای را در یک فایل می‌نویسد:

var fs = require('fs'); fs.writeFile('data.txt', 'Hello World!', function (err) { if (err) { throw err; } console.log('It is saved!'); });

بسیاری از توابع API دارای نسخه های همگام(synchronous) می‌باشد. اسکریپت قبل بصورت زیر با writeFilesync می‌تواند نوشته شود :

fs.writeFileSync('data.txt', 'Hello world');

خواندن فایل نیز تقریبا بصورت قبل است با این تفاوت که باید از متد readFile بصورت زیر استفاده کنیم:

var fs = require('fs'); fs.readFile('data.txt', function(err, data) { if (err) { throw err; } console.log(data.toString()); });

کار با رویدادها (events)

الگوی طراحی ناظر به طور گسترده در دنیای جاوا اسکریپت استفاده می شود. ایجا جایی است که اشیاء در سیستم شما در اشیاء دیگر تغییر یافته و به اشتراک گذاشته می شوند. Node.js شامل ماژول از پیش ساخته شده است که رویدادها را مدیریت می کند. در زیر مثالی را

مشاهده می کنید:

var events = require('events'); var eventEmitter = new events.EventEmitter(); var somethingHappen = function() { console.log('Somthing happen!'); }; eventEmitter .on('something-happen', somethingHappen) .emit('something-happen');

شیء eventEmitter شیء است که ما آن را به اشتراک گذاشته ایم. این کار را با استفاده از متد on انجام می دهیم. تابع emit رویداد را راه اندازی می کند و somethingHappen اجرا می شود.

ماژول events قابلیت های ضروری را فراهم می کند، اما نیاز داریم که از آنها در کلاس های خود استفاده کنیم. اجازه دهید مثال book که در بخش قبلی دیدیم را با رویدادها انجام دهیم. هنگامی که شخصی به کتاب رای می دهد، ما به شیوه زیر رویداد را مخابره خواهیم کرد:

// book.js var util = require("util"); var events = require("events"); var Class = function() { }; util.inherits(Class, events.EventEmitter); Class.prototype.ratePoints = 0; Class.prototype.rate = function(points) { ratePoints = points; this.emit('rated'); }; Class.prototype.getPoints = function() { return ratePoints; } module.exports = Class;

در این مثال می خواهیم رفتار شیء EventEmitter را به ارث ببریم. ساده ترین راه برای انجام این کار با استفاده از Node.js استفاده از ماژول util می باشد و متد inherits آن. کلاس تعریف شده می تواند بصورت زیر باشد:

var BookClass = require('./book.js'); var book = new BookClass(); book.on('rated', function() { console.log('Rated with ' + book.getPoints()); }); book.rate(10);

ما دوباره از متد on برای به اشتراک گذاری در رویداد rated استفاده کرده ایم. کلاس book پیام هایی را که مشخص کرده ایم را نمایش می دهد. آنگاه ترمینال نوشته Rated with 10 چاپ می کند.

مدیریت فرآیندهای فرزند

برخی چیزها را نمی توانیم با Node.js انجام دهیم. نیاز داریم که از برنامه های خارجی استفاده کنیم. خبر خوب این است که می توانیم دستورات shell در یک اسکریپت Node.js اجرا کنیم.

به عنوان مثال، اجازه دهید فرض کنیم که می خواهیم فایل های موجود در یک دایرکتوری را لیست کنیم. API های فایل سیستم روش هایی را برای انجام این کار فراهم می کنند، اما این امر وقتی می تواند خوب باشد که خروجی مثل دستور ls دریافت کنیم:

// exec.js var exec = require('child_process').exec; exec('ls -l', function(error, stdout, stderr) { console.log('stdout: ' + stdout); console.log('stderr: ' + stderr); if (error !== null) { console.log('exec error: ' + error); } });

در کد فوق ماژولی که استفاده کردیم child_process است. متد آن یعنی exec دستور مورد نظر را به عنوان رشته و یک callback می پذیرد. آیتم بعدی (stdout) خروجی دستور می‌باشد. اگر بخواهیم هر خطایی را مورد بررسی قرار دهیم از شیء error یا از بافر داده stderr استفاده می‌کنیم.

کد قبل بصورت زیر در خروجی نمایش داده می‌شود:

$ node index.js Stdout:total 2 -2w-2--2-- 1lucy administ 241 sep 2 13:10 index.js D2wx2-x2-x 1lucy administ 0Aug 27 13:24 node_modules -2w-2--2-- 1lucy administ 83 sep 1 17:44 package.json -2w-2--2-- 1lucy administ 180 Aug 27 13:25 server .js

در کنار متد exec ، ما spawn را نیز داریم. تفاوت اندکی بین این دو وجود دارد که جالب است.

فرض کنید که دستوری داریم که نه تنها کار خودش را انجام می دهد، بلکه خروجی ها را نیز نشان می دهد. به عنوان مثل git push ممکن است چند ثانیه طول بکشد و آنگاه پیام ها را بطورمداوم به کنسول ارسال کند. در اینگونه موارد، spawn یک نوع خوب است:

var spawn = require('child_process').spawn; var command = spawn('git', ['push', 'origin', 'master']); command.stdout.on('data', function (data) { console.log('stdout: ' + data); }); command.stderr.on('data', function (data) { console.log('stderr: ' + data); }); command.on('close', function (code) { console.log('child process exited with code ' + code); });

در اینجا stdout و stderr جریان (stream) ما می باشند. آنها رویدادها را مخابره می کنند و اگرما این رویدادها را به اشتراک بگذاریم، همان خروجی دستور را دریافت می کنیم که تولید شده بود. در مثال قبل، ما git push orogin master را اجرا کردیم و پاسخ کامل دستور را به کنسول ارسال شد.



نظرات کاربران



عبارت امنیتی : حاصل 8 × 6 می شود :
متن پیام :