цикъл на събитие във възел js

Cik L Na S Bitie V V V Zel Js



Node.js е мощна Javascript рамка, която позволява на потребителите да изпълняват Javascript код на сървъра извън браузъра. Това е неблокираща среда за изпълнение, управлявана от събития за изграждане на надеждни мащабируеми уеб приложения. Цикълът на събитията е важна част от Node.js, която ви позволява да изпълнявате задачи, без да чакате една да приключи, преди да започнете друга.

Въпреки че Javascript е еднонишков език, Node.js може да възлага задачи на операционната система, което й позволява да обработва множество задачи едновременно. Няколко задачи трябва да бъдат изпълнени едновременно, тъй като операциите в операционната система са многонишкови. Обратното извикване, свързано с всяка операция, се добавя към опашката на събития и се планира от Node.js да се изпълни, когато определената задача бъде завършена.

За да напише ефективен и надежден Node.js код, потребителят трябва да има солидно разбиране за цикъла на събитията. Може също така да помогне за ефективно отстраняване на проблеми с производителността. Цикълът на събитията в Node.js спестява памет и ви позволява да правите няколко неща наведнъж, без да се налага да чакате всяко от тях да приключи. Терминът „асинхронен“ се отнася до всяка Javascript функция, която работи във фонов режим, без да блокира входящите заявки.







Преди да преминем директно към цикъла на събитията, нека да разгледаме различни аспекти на езика за програмиране Javascript.



Javascript като асинхронен език за програмиране

Нека да разгледаме концепциите на асинхронното програмиране. Javascript се използва в уеб, мобилни и настолни приложения, но трябва да се отбележи, че Javascript е еднопоточен, синхронен език за компютърно програмиране.



Даден е прост пример за код, за да се разбере концепцията.





функционален метод1 ( ) {

конзола. дневник ( 'Функция 1' )

}

функционален метод2 ( ) {

конзола. дневник ( 'Функция 2' )

}

метод1 ( )

метод2 ( )

В този код се създават две прости функции и първо се извиква метод1, така че първо да регистрира метод1 и след това да премине към следващия.

Изход



Javascript като синхронен език за програмиране

Javascript е синхронен език за програмиране и изпълнява всеки ред стъпка по стъпка, като се движи отгоре надолу, като се изпълнява само един ред наведнъж. В дадения по-горе примерен код method1 се регистрира първо в терминала и след това method2.

Javascript като блокиращ език

Тъй като е синхронен език, javascript има блокираща функционалност. Няма значение колко време отнема завършването на текущ процес, но нов процес няма да бъде стартиран, докато предишният не бъде завършен. В горния пример с код да предположим, че има много кодов скрипт в method1, без значение колко време отнема 10 секунди или минута method2 няма да бъде изпълнен, докато не бъде изпълнен целият код в method1.

Потребителите може да са изпитали това, докато сърфират. Когато уеб приложение се изпълнява в браузър в задния край, се изпълнява огромно парче код, така че браузърът изглежда замразен за известно време, преди да върне контролния достъп на потребителя. Това поведение е известно като блокиране. Браузърът не може да приема други входящи заявки, докато текущата заявка не бъде обработена.

Javascript е език с една нишка

За да стартирате програма в javascript се използва функционалността на нишката. Нишките могат да изпълняват само една задача наведнъж. Други езици за програмиране поддържат многопоточност и могат да изпълняват множество задачи паралелно, javascript съдържа само една нишка за изпълнение на всеки кодов скрипт.

Изчакване в Javascript

Както е видно от името в този раздел, трябва да изчакаме нашата заявка да бъде обработена, за да продължим. Чакането може да отнеме няколко минути, през които не се приемат допълнителни заявки. Ако кодовият скрипт продължи без изчакване, кодът ще срещне грешка. Някои функции трябва да бъдат внедрени в Javascript или по-конкретно Node.js, за да направят кода асинхронен.

Сега, след като разбрахме различните аспекти на Javascript, нека разберем синхронния и асинхронния чрез някои прости примери.

Синхронно изпълнение на код в Javascript

Синхронно означава, че кодът се изпълнява последователно или по-просто стъпка по стъпка, като се започне отгоре и се движи надолу ред по ред.

По-долу е даден пример, който може да помогне за разбирането:

// application.js

конзола. дневник ( 'един' )

конзола. дневник ( 'Две' )

конзола. дневник ( 'Три' )

В този код има три оператора console.log, всеки от които отпечатва нещо. Първо първият оператор, който ще отпечата „One“ в конзолата, се изпраща в стека за повиквания за 1 ms (приблизително), след което се регистрира в терминала. След това второто изявление се избутва в стека за повиквания и сега времето е 2 ms с едно добавяне от предишното и след това регистрира „Две“ в конзолата. И накрая, последното изявление се избутва в стека за повиквания, тъй като сега времето е 3ms и се записва „Три“ в конзолата.

Горният код може да бъде изпълнен чрез извикване на следната команда:

възлово приложение. js

Изход

Функционирането е обяснено по-горе подробно и като се има предвид, изходът се регистрира в конзолата в миг на око:

Асинхронно изпълнение на код в Javascript

Сега нека преработим същия код, като въведем обратни извиквания и направим кода асинхронен. Горният код може да бъде преработен като:

// application.js
функция printOne ( обратно повикване ) {
setTimeout ( функция ( ) {
конзола. дневник ( 'един' ) ;
обратно повикване ( ) ;
} , 1000 ) ;
}
функция printTwo ( обратно повикване ) {
setTimeout ( функция ( ) {
конзола. дневник ( 'две' ) ;
обратно повикване ( ) ;
} , 2000 г ) ;
}
функция printThree ( ) {
setTimeout ( функция ( ) {
конзола. дневник ( 'Три' ) ;
} , 3000 ) ;
}
конзола. дневник ( 'Старт на програмата' ) ;
printOne ( функция ( ) {
printTwo ( функция ( ) {
printThree ( ) ;
} ) ;
} ) ;
конзола. дневник ( 'Край на програмата' ) ;

В този код по-горе:

  • Три функции са декларирани да отпечатват „One“, „Two“ и „Three“, всяка функция има параметър за обратно извикване, който позволява последователно изпълнение на код.
  • Времето за изчакване се задава с помощта на функцията setTimeout и има оператор console.log за отпечатване след определено забавяне.
  • Отпечатват се две съобщения „Старт на програмата“ и „Край на програмата“, които показват началото и края на програмата.
  • Програмата стартира с отпечатване на „Старт на програмата“, след което функцията printOne се изпълнява с 1 секунда закъснение, след това функцията printTwo се изпълнява с 2 секунди закъснение и накрая функцията printThree се изпълнява с 3 секунди забавяне.
  • Програмата не изчаква изпълнението на асинхронен код във функциите setTimeouts, които регистрират оператора „Край на програмата“, преди да отпечатат One, Two и Three.

Изход

Стартирайте горния код, като изпълните тази команда в терминала:

възлово приложение. js

Сега изходът в терминала ще се покаже асинхронно като:

Сега, когато имаме пълно разбиране за синхронното и асинхронното изпълнение, нека преминем към затвърждаване на нашата концепция за цикъл на събития в Node.js.

Node.js: Механизъм за цикъл на събития

Изпълнението както на синхронни, така и на асинхронни задачи се управлява от цикъла на събитията в Node.js. Изпълнението се извиква веднага щом се стартира проектът Node.js и плавно прехвърля сложните задачи към системата. Това гарантира, че други задачи могат да се изпълняват гладко в основната нишка.

Визуално обяснение на цикъла на събитията в Node.js

Цикълът на събитията е непрекъснат и полубезкраен в Node.js. Цикълът на събитията се извиква от началото на кодовия скрипт на Node.js и е отговорен за извършването на асинхронни API извиквания и извикването на процеси. Tick() и таймерите за планиране след това възобновяват изпълнението на цикъла на събитията.

В Node.js пет основни типа опашки обработват обратни извиквания:

  • „Опашката на таймера“, известна като min-heap, отговаря за обработката на обратни извиквания, свързани с „setTimeout“ и „setInterval“.
  • Обратните извиквания за асинхронни операции като в модулите „fs“ и „http“ се обработват от „I/O Queue“.
  • „Опашката за проверка“ съдържа обратни извиквания за функцията „setImmediate“, която е уникална за Node.
  • „Опашката за затваряне“ управлява обратни извиквания, свързани със събитието за затваряне на всяка асинхронна задача.
  • И накрая, има две различни опашки в опашката „Микро задачи“:
    • Опашката „nextTick“ съдържа обратни извиквания, свързани с функцията „process.nextTick“.
    • Опашката „Promise“ контролира обратните извиквания, свързани с родния Promise.

Функционалност на Event Loop в Node.js

Цикълът на събитията функционира при специфични изисквания, които контролират реда за изпълнение на обратно извикване. Синхронният код на Javascript на потребителя получава приоритет в началото на процеса, така че цикълът на събитията започва само когато стекът на повикванията е изчистен. Следната последователност на изпълнение следва структуриран модел:

Най-високият приоритет се дава на обратните извиквания в опашката с микрозадачи, след което се преминава към изпълнение на задачите в опашката nextTick, последвани от задачите в опашката Promise. След това процесите в обратните извиквания на опашката на таймера се обработват, след което опашката с микрозадачи се посещава отново след всяко обратно извикване на таймера. Обратните извиквания в опашките за вход/изход, проверка и затваряне след това се изпълняват по подобен модел с опашката на микрозадачи, посещавана след всяка фаза.

Цикълът продължава да се изпълнява, ако има още обратни извиквания за обработка. Когато кодовият скрипт е приключил или не са останали обратни извиквания за обработка, цикълът на събитията приключва ефективно.

Сега, след като разбираме дълбоко цикъла на събитията, нека да разгледаме неговите характеристики.

Характеристики на цикъл на събития в Node.js

Основните характеристики са:

  • Цикълът на събитията е безкраен цикъл и продължава да изпълнява задачите веднага щом ги получи и преминава в режим на заспиване, в случай че няма задачи, но започва да функционира веднага щом задачата бъде получена.
  • Задачите в опашката на събития се изпълняват само когато стекът е празен, което означава, че няма активна операция.
  • Обратните повиквания и обещанията могат да се използват в цикъла на събитията.
  • Тъй като цикълът на събитията следва принципа на опашката от абстрактни типове данни, той изпълнява първата задача, след което преминава към следващата.

След задълбочено разбиране на цикъла на събитията и логиката на асинхронните и синхронни изпълнения, разбирането на различните фази може да затвърди концепциите за цикъла на събитията.

Node.js Фази на цикъл на събития

Както бе споменато по-горе, цикълът на събитията е полубезкраен. Има много фази, но някои фази се използват за вътрешна обработка. Тези фази нямат ефект върху кодовия скрипт.

Цикълът на събитията следва функционалността на Queue и изпълнява задачата на принципа първи влязъл и първи излязъл. Планираните таймери ще се обработват от операционната система, докато изтекат. След това изтеклите таймери се добавят към опашката за обратно извикване за таймери.

Цикълът на събитията изпълнява задачите в опашката на таймера една по една, докато не останат повече задачи или достигне максималния разрешен брой задачи. В разделите по-долу са обяснени основните фази на цикъла на събитията.

Таймери Фаза

В Node.js има API за таймер, който може да планира функциите, които трябва да бъдат изпълнени в бъдеще. След като определеното време изтече, обратното извикване на таймера ще се изпълни веднага щом може да бъде планирано; въпреки това може да възникне забавяне или от края на операционната система, или поради изпълнение на други обратни извиквания.

API на таймерите има три основни функции:

  • setTimeout
  • setImmediate
  • setInterval

Гореспоменатите функции са синхронни. Фазата на таймера в цикъла на събитията има обхват, ограничен до функциите setTimeout и setInterval. Докато функцията за проверка обработва функцията setImmediate.

Нека разгледаме един прост пример, за да затвърдим теоретичната част:

// application.js

функция delayedFunction ( ) {

конзола. дневник ( 'отложената функция се изпълнява след изчакване' ) ;

}

конзола. дневник ( 'Старт на програмата' ) ;

setTimeout ( забавена функция, 2000 г ) ;

конзола. дневник ( 'Край на програмата' ) ;

В този код:

  • Програмата стартира чрез регистриране на оператора „Старт на програмата“ в терминала.
  • След това delayedFunction се извиква с таймер от 2 ms, кодовият скрипт не спира и продължава по-нататъшно обработване на забавянето във фонов режим.
  • Изявлението „Край на програмата“ се регистрира след първото изявление.
  • След забавяне от 2 ms, изразът в delayedFunction се записва в терминала.

Изход

Резултатът ще се покаже като:

Може да се види, че кодът не е спрян за обработка на delayedFunction; той се придвижва напред и след забавянето се обработва обратното извикване на функцията.

Чакащи обратни повиквания

Цикълът на събитията проверява за случващите се събития, като четене на файлове, мрежови дейности или входно/изходни задачи, във фазата на запитване. Важно е да знаете, че в Node.js само някои от събитията се обработват в тази фаза на анкета. Въпреки това, в последващата итерация на цикъла на събитията, някои събития могат да бъдат отложени до чакащата фаза. Това е ключова концепция, която трябва да имате предвид, когато оптимизирате и отстранявате проблеми с Node.js код, който включва сложни операции, управлявани от събития.

Важно е да разберете, че по време на фазата на чакащи обратни извиквания, цикълът на събитията добавя отложени събития към опашката от чакащи обратни извиквания и ги изпълнява. Тази фаза също така обработва някои грешки на TCP сокет, които системата е генерирала, като например събития за грешка ECONNREFUSED на определени операционни системи.

По-долу е споменат пример за затвърждаване на концепцията:

// application.js
конст fs = изискват ( 'fs' ) ;
функция readFileAsync ( filePath, обратно извикване ) {
fs. readFile ( './PromiseText.txt' , 'utf8' , функция ( грешка, данни ) {
ако ( грешка ) {
конзола. грешка ( ` Грешка файл за четене : $ { грешка съобщение } ` ) ;
} друго {
конзола. дневник ( ` Файл съдържание : $ { данни } ` ) ;
}
обратно повикване ( ) ;
} ) ;
}
конзола. дневник ( 'Старт на програмата' ) ;
readFileAsync ( './PromiseText.txt' , функция ( ) {
конзола. дневник ( „Обратното извикване за четене на файл е изпълнено“ ) ;
} ) ;
конзола. дневник ( 'Край на програмата' ) ;

В този код:

  • Програмата се стартира чрез регистриране на оператора „Старт на програмата“ в терминала.
  • ReadFileAsync е дефиниран асинхронно за четене на съдържанието на файла „PromiseText.txt“. Това е параметризирана функция, която изпълнява функция за обратно извикване, след като файлът е прочетен.
  • Функцията readFileAsync се извиква, за да започне процеса на четене на файл.
  • В процеса на четене на файла програмата не спира; вместо това той преминава към следващия оператор и го регистрира в терминала „Край на програмата“.
  • Асинхронното събитие за четене на файл се обработва във фонов режим от цикъла на събитията.
  • След като файлът бъде прочетен асинхронно и съдържанието е регистрирано в терминала, програмата регистрира съдържанието на файла в терминала. След това регистрира следното съобщение „Обратното извикване за четене на файл е изпълнено“.
  • Цикълът за събития обработва чакащите операции за обратно извикване в следващата фаза.

Изход

Резултатът от горното изпълнение е:

Неактивен, фаза на подготовка в Node.js

Фазата на неактивност се използва за работа с вътрешни функции в Node.js, така че не е стандартна фаза. Това не влияе на кодовия скрипт. Фазата на неактивност е като период на почивка за цикъла на събитията, по време на който се управляват задачите с нисък приоритет във фонов режим. Прост пример за разбиране на тази фаза е:

конст { празен } = изискват ( 'idle-gc' ) ;

празен. игнорирайте ( ) ;

В този код се използва модулът „idle-gc“, който позволява да се игнорира фазата на празен ход. Това служи за справяне със ситуации, когато цикълът на събития е зает и фоновите задачи не се изпълняват. Използването на idle.ignore не се счита за оптимално, тъй като може да причини проблеми с производителността.

Фаза на анкетиране в Node.js

Фазата на анкетата в Node.js служи като:

  • Той обработва събитията в опашката за анкети и изпълнява съответните им задачи.
  • Той решава колко време да прекара в изчакване и проверка на I/O операциите в процеса.

Когато цикълът на събитията навлезе във фазата на анкета поради липса на таймер, ще бъде изпълнена една от задачите по-долу:

  • Във фазата на анкета на цикъла на събитията в Node.js чакащите I/O събития се поставят на опашка и след това се изпълняват в последователна процедура, следваща принципа „Първи влезли и първи излезли“, докато опашката се изпразни. По време на изпълнението на обратните извиквания опашките nextTick и microtasks също са в действие. Това гарантира плавност и позволява по-ефективно и надеждно управление на I/O операциите.
  • Ако опашката е празна и скриптът не е планиран от функцията setImmediate(), тогава цикълът на събитията ще приключи и ще продължи към следващата фаза (проверка). От друга страна, ако планирането на скрипта е извършено от функцията setImmediate(), цикълът на събитията позволява обратните извиквания да бъдат добавени към опашката, която ще бъде изпълнена от него.

Това е най-добре илюстрирано с прост примерен код:

setTimeout ( ( ) => {

конзола. дневник ( „Асинхронната операция е завършена“ ) ;

} , 2000 г ) ;

конзола. дневник ( 'Старт' ) ;

setImmediate ( ( ) => {

конзола. дневник ( 'setImmediate обратно извикване изпълнено' ) ;

} ) ;

конзола. дневник ( 'Край' ) ;

В този код:

  • Две съобщения „Старт“ и „Край“ показват стартирането и прекратяването на програмата.
  • Функцията setTimeout() задава функция за обратно извикване със закъснение от 2 ms и регистрира „Асинхронна операция завършена“ на терминала.
  • Функцията setImmediate() регистрира съобщението „setImmediate callback executed“ към терминала, след като съобщението Start е регистрирано към терминала.

Изход

Изходът ще покаже съобщенията само с кратко наблюдение, че „Асинхронната операция е завършена“ отнема време и се отпечатва след съобщението „Край“:

Node.js Проверка на фазата

След като фазата на анкета е изпълнена, се изпълняват обратните извиквания във фазата на проверка. Ако кодовият скрипт е планиран с помощта на функцията setImmediate() и функцията за анкета е безплатна, цикълът на събитията работи, като се придвижва директно към фазата на проверка, вместо да остава неактивен. Функцията setImmediate() е уникален таймер, който работи по време на различните фази на цикъла на събитията.

API на libuv се използва за планиране на изпълненията на обратно извикване след завършване на изпълнението на фазата на проучване. По време на изпълнението на кода цикълът на събития навлиза във фазата на анкета, в която чака входящите заявки за връзка. В друг случай, ако обратното извикване е планирано с помощта на функцията setImmediate() и фазата на анкета е прекратена без никаква дейност, тя ще премине към фазата на проверка, вместо да чака. Разгледайте примера по-долу, за да разберете:

// application.js

конзола. дневник ( 'Старт' ) ;

setImmediate ( ( ) => {

конзола. дневник ( „Незабавно обратно повикване“ ) ;

} ) ;

конзола. дневник ( 'Край' ) ;

В този код три съобщения се влизат в терминала. След това функцията setImmediate() накрая изпраща обратно извикване, за да регистрира съобщението ' Незабавно обратно обаждане ” към терминала.

Изход

Резултатът от горния код ще се покаже в следната последователност:

Node.js затваря обратни извиквания

Node.js използва тази фаза на затваряне, за да изпълнява обратни извиквания за затваряне на събития и прекратяване на итерация на цикъл на събитие. След като връзката е затворена, цикълът на събития обработва затварящите събития в тази фаза. В тази фаза на цикъла на събитията „nextTick()“ и микрозадачите се генерират и обработват подобно на други фази.

Функцията process.exit се използва за прекратяване на цикъла на събития във всеки един момент. Цикълът на събитията ще пренебрегне всички чакащи асинхронни операции и процесът Node.js ще бъде прекратен.

Прост пример за разглеждане е:

// application.js
конст нето = изискват ( 'мрежа' ) ;
конст сървър = нето. createServer ( ( гнездо ) => {
гнездо. На ( 'близо' , ( ) => {
конзола. дневник ( „Гнездото е затворено“ ) ;
} ) ;
гнездо. На ( 'данни' , ( данни ) => {
конзола. дневник ( „Получени данни:“ , данни. toString ( ) ) ;
} ) ;
} ) ;
сървър. На ( 'близо' , ( ) => {
конзола. дневник ( „Сървърът е затворен“ ) ;
} ) ;
конст порт = 3000 ;
сървър. слушам ( порт, ( ) => {
конзола. дневник ( `Сървър слуша на порт $ { порт } ` ) ;
} ) ;
setTimeout ( ( ) => {
конзола. дневник ( „Сървърът се затваря след 10 секунди“ ) ;
сървър. близо ( ) ;
процес. изход ( ) ;
} , 10 000 ) ;

В този код:

  • const net = изискване ('net') ' импортира мрежовия модул, необходим за работа с TCP сървър и ' const сървър = net.createServer((socket) => { ” създава нов екземпляр на TCP сървър.
  • socket.on(‘close’, () => {… } ” слуша “close” на всички гнезда. Когато връзката на сокета е затворена, в терминала се регистрира съобщението „Сокетът е затворен“.
  • socket.on(‘данни’, (данни) => {} ” проверява за входящи данни от всички отделни сокети и ги отпечатва с помощта на функцията “.toString()”.
  • server.on(‘close’, () => {…} ” проверява за събитието „затваряне” на самия сървър и когато връзката със сървъра е затворена, регистрира съобщението „Сървърът е затворен” на терминала.
  • server.listen(port, () => {…} ” слуша входящите връзки на порта.
  • setTimeout(() => {…} ” задава таймер от 10 ms за затваряне на сървъра.

Това приключва дискусията за различните фази на цикъла на събитията в Node.js. Преди да пристъпим към заключение, нека обсъдим едно последно нещо, което е как да излезете от цикъла на събитията в Node.js.

Излизане от цикъла на събитията в Node.js

Цикълът на събития е във фаза на изпълнение, докато има някои задачи във всички опашки от фази на цикъл на събития. Цикълът на събитието приключва след излъчване на изходната фаза и обратното извикване на слушателя за изход се връща, ако няма повече задачи в опашките.

Изричният начин за прекратяване на цикъл на събитие е да се използва методът „.exit“. Активните процеси на Node.js ще прекратят незабавно веднага щом се извика функцията process.exit. Всички планирани и чакащи събития ще бъдат премахнати:

процес. На ( 'изход' , ( код ) => {

конзола. дневник ( `Излизане с код за изход : $ { код } ` ) ;

} ) ;

процес. изход ( 1 ) ;

Потребителите могат да слушат функцията .exit. Трябва да се отбележи, че функцията „.exit“ трябва да е синхронна, тъй като програмата Node.js ще излезе веднага щом прослуша това събитие.

Това приключва дискусията относно цикъла на събитията. Задълбочена статия, която обхваща всички концепции, фази и примери, свързани с цикъла на събитията.

Заключение

Преди да разберете цикъла на събитията, прегледът на синхронните и асинхронните концепции може да помогне за разбирането на кодовия поток в цикъла на събитията. Синхронното изпълнение означава изпълнение стъпка по стъпка, докато асинхронното изпълнение означава спиране на някои стъпки, без да се чака тяхното завършване. Работата на цикъла на събитията заедно с всички фази заедно с подходящи примери са обсъдени в статията.