Или защо трябва да внимавате, когато работите с потоци и излъчватели на събития като цяло

Пример

За да демонстрирам този проблем, ще използвам Node.js потоци:

Потокът е структура, която позволява на потребителите да четат/записват данни последователно от/към нея. Потоците се използват главно при работа с голямо количество данни. Когато искате да преместите голяма част от данни от едно място на друго (например от файл в нов файл), по-добре е да използвате поточно предаване, така че не е необходимо да съхранявате цели данни в паметта (може дори да е невъзможно, ако данните са твърде голям). "Прочетете още".

Създаваме поток Writable (този, който позволява само запис на данни) и веднага след това го унищожаваме. Функцията destroy() приема като параметър грешка, която се хвърля, когато потокът се използва след унищожаване. След това се опитваме да използваме поток и очакваме да получим грешка в catch блок.

Както може би вече се досещате, не е уловен!

Какво става?

Отговорът на този въпрос е, че потокът е подклас на EventEmitter object, който използва асинхронно обратно извикване.

Хм, но какво означава? Какво е това EventEmitter? Защо е толкова специално?

Излъчвател на събития

Излъчвателят на събития е обект, предоставен от Node.js за работа със системата за събития (той е част от вградения пакет за събития). Позволява ни да изпращаме събития и да ги обработваме със слушатели. Кодът говори повече от хиляди думи, така че ето пример за използване на основен източник на събития:

Създаваме нов EventEmitter екземпляр и прикачваме манипулатор на събитие към 'myEvent' събитие. Сега всеки път, когато се излъчва 'myEvent', ще се извиква функция eventHandler(). След това излъчваме това събитие и очакваме манипулаторът да бъде уволнен.

И резултатът е както се очаква:

Ами ако хвърлим грешка вътре в манипулатора? И обградете всичко с опит-улов. Да видим:

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

И след стартиране на скрипта можем да видим, че грешката не еуловена:

Ако се замислите, това е логично поведение. Изпращаме асинхронен манипулатор и никъде не го чакаме (нито използването му). Обратното извикване на източника на събитие се извиква независимо от try-catch, така че никога не се улавя.

Как да направим така, че да не срине приложението?

Обработване на грешки в излъчвателя на събития

Обектите за излъчване на събития ни дават специално събитие за обработка на грешките. Всеки път, когато се случи грешка в екземпляр на излъчвател на събитие, се излъчва специално 'error' събитие. Но трябва имплицитно да помолим излъчвателя на събитие да действа по този начин, като изпрати { captureRejections: true } вътре в конструктор.

Всичко, което трябва да направим сега, е да прикачим обратно извикване към събитието 'error':

В момента нито едно приложение не се срива.

Пример за коригиране на потоци

Помните ли първия пример? Сега трябва да е лесно да се коригира с това знание. За потоци не е необходимо да задаваме captureRejection: true. Зададено е по подразбиране.

Всичко, което е необходимо, е обратно извикване на събитие 'error':

Малка промяна на кода, която може да предотврати сривове на вашия производствен код в непредсказуеми моменти.

Как да избегнем тези необработени грешки?

Доста просто, просто трябва да настроите обратно извикване за събитие 'error'. Истинският въпрос е как ще разберете дали работите с подклас на излъчвател на събития?

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

  • Потоци (require('stream')) — вече говорихме за тях
  • Net (require('net')) — използва се за работа в мрежа
  • CreateReadStream() от fs пакет ({createReadStream} = require(‘fs’)) — използва се за създаване на потоци от файлове (попада в категорията потоци, но е толкова популярен, че реших да го отбележа тук).

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

За да сте сигурни, можете да разгледате внедряването и да проверите дали то разширява EventEmitter или има EventEmitter във веригата прототип. Проверката на документацията също ще бъде полезна.

Последни бележки

Започнахме с опростен, но пример от реалния свят. Някой разработчик е използвал stream в try-catch и е бил сигурен, че приложението никога няма да се срине. Няколко пъти седмично приложението се срива без причина. Той работеше усилено, за да намери проблем, но нямаше представа.

„Мястото, когато се случва катастрофа, е вътре в опит-улов. Трябва да се хване! Къде се изплъзват тези грешки?“

Докато не разбра за излъчвателите на събития и всичко мигновено се изясни.

Надяваме се, че няма да направите тази грешка.

Излъчвателите на събития са много интересна тема. За да прочетете повече, проверете.

Създавайте композируеми уеб приложения

Не изграждайте уеб монолити. Използвайте Bit, за да създавате и съставяте отделени софтуерни компоненти — в любимите си рамки като React или Node. Създавайте мащабируеми и модулни приложения с мощно и приятно изживяване за разработчици.

Доведете екипа си в Bit Cloud, за да хоствате и да си сътрудничите по компоненти заедно и да ускорите, мащабирате и стандартизирате разработката като екип. Опитайте компонентни интерфейси с Система за проектиране или Микро интерфейси или изследвайте компонентни интерфейси с сървърни компоненти .

Опитайте →

Научете повече