Node.js Generators & Сравнете с обратни повиквания
⚡ Умно обобщение
GeneratorВ Node.js са функции, чието изпълнение може да бъде спряно и възобновено при поискване. Ключовата дума yield поставя функцията на пауза, методът next я възобновява и това сдвояване изравнява дълбоко вложените обратни извиквания в четлив последователен код.

В това ръководство ще разгледаме генераторите и как те се различават от обратните извиквания.
Какво представляват генераторите?
Generatorстана популярен в Node.js заради това, което са способни да правят.
- Generators са изпълнения на функции, които могат да бъдат спрени и възобновени на по-късен етап.
- Generatorса полезни за концепции като „лениво изпълнение“. Чрез спиране на изпълнението и възобновяване по желание, стойностите се извличат само когато са необходими.
GeneratorИмат двата ключови метода по-долу.
- Метод на добива — извиква се вътре във функция, за да спре изпълнението на точния ред, където
yieldПоявява се. - Следващ метод — извиква се от главното приложение за възобновяване на функция, която е дала резултат. Изпълнението продължава до следващото
yield, или до края на функцията.
Нека разгледаме пример за това как могат да се използват генератори. Примерът дефинира проста функция за добавяне, която събира две числа, спирайки изпълнението в различни точки, за да покаже как се държат генераторите.
function* Add(x) { yield x + 1; // The value passed into the second next() call lands here var y = yield(null); return x + y; } var gen = Add(5); gen.next(); // runs to the first yield, returns { value: 6, done: false } gen.next(6); // resumes, y becomes 6, returns { value: 11, done: true }
Code Обяснение
- Първата стъпка е да се дефинира генераторната функция. Обърнете внимание на
*добавен къмfunctionключова дума. Функцията се извикваAddи приема параметърx. - -
yieldКлючовата дума е специфична за генераторите и паузира функцията по време на изпълнение. Изпълнението спира тук, докатоnext()се извиква. - Вторият
yieldедновременно паузира функцията и получава стойност: каквото и да се предаде на следнотоnext()обаждането става стойността наy. - повикване
gen.next()първия път функцията се изпълнява до първияyieldОбажданеgen.next(6)възобновява го, присвоява 6 наyи се връщаx + y.
⚠️ Корекция: оригиналната обява съдържаше реда y = 6 веднага след var y = yield(null);Това присвояване презаписва каквато и да е стойност next() доставки, което обезсмисля целта на примера и прави втория yield безсмислено. Стойността сега се предава чрез gen.next(6), което е начинът, по който генераторът всъщност получава данни от повикващия.
Обратни повиквания срещу генератори
Generatorрешават проблема, известен като „адът на обратните извиквания“. Функциите за обратни извиквания понякога стават толкова дълбоко вложени по време на разработката на Node.js приложение, че е трудно да се следват.
Тук помагат генераторите. Един от най-често срещаните примери е създаването на таймерни функции. Примерът по-долу изгражда проста функция за забавяне във времето и след това я извиква със закъснения от 1000, 2000 и 3000 ms.
Стъпка 1) Дефинирайте функцията за обратно извикване с необходимия код за времезакъснение.
function Timedelay(ptime, callback) { setTimeout(function() { callback("Pausing for " + ptime); }, ptime); // the delay must use ptime, the parameter name }
Code Обяснение
- Функция, наречена
Timedelayсе създава с параметърptime, който съдържа броя милисекунди, за които да се направи пауза. setTimeoutизчаква този брой милисекунди и след това извиква предоставената обратна функция.
⚠️ Корекция: оригиналният код е приет time като втори аргумент за setTimeoutно няма посочена променлива time съществува — параметърът е ptimeКакто е написано, хвърля ReferenceError: time is not defined.
Стъпка 2) Сега разгледайте кода, използващ обратни извиквания. За да се свържат закъснения от 1000, 2000 и 3000 милисекунди, обратните извиквания се влагат едно в друго.
Timedelay(1000, function(message) { console.log(message); Timedelay(2000, function(message) { console.log(message); Timedelay(3000, function(message) { console.log(message); }); }); });
Code Обяснение
Timedelayсе извиква като обратно извикване със стойност 1000.- Вътре в това обратно извикване,
Timedelayсе извиква отново с 2000. - Вътре в този,
Timedelayсе извиква трети път с 3000.
⚠️ Корекция: всяко обратно извикване в оригиналния регистриран файл msg, но параметърът е именуван messageИ трите линии хвърлиха ReferenceError: msg is not defined.
От горния код можете да видите, че става все по-хаотично с всяко допълнително извикване — това влагане е това, което callback hell описва.
Стъпка 3) Сега вижте същото поведение, записано с генератори. Последователността се чете като три плоски линии, а не като три нива на влагане.
function* Messages() { console.log(yield Timedelay(1000)); console.log(yield Timedelay(2000)); console.log(yield Timedelay(3000)); }
Code Обяснение
- Дефинирана е генераторна функция, която извиква
Timedelayфункция. yieldсе използва сTimedelayи 1000 като стойност на параметъра.yieldсе използва отново с 2000 и още веднъж с 3000.
⚠️ Корекция: оригиналът е написал console,log(...) със запетая вместо точка и на трите реда, което е синтактична грешка и изобщо предотвратява парсирането на файла.
Как да стартирате a Generator с функция за бегач
Генераторът по-горе се чете прекрасно, но не прави нищо самостоятелно. Това е детайлът, който повечето обяснения пропускат, и това е причината плоската версия да изглежда като магия. Генераторът е машина на пауза и нещо трябва да натиска непрекъснато бутона. Извикване Messages() просто създава обекта генератор и връща резултата веднага; таймерът никога не се стартира и нищо не се отпечатва.
Липсващото парче е runner: малка функция, която извиква next(), изчаква асинхронната работа да приключи и след това извиква next() отново с резултата.
// Timedelay now hands its result back to the runner function Timedelay(ptime) { return function(resume) { setTimeout(function() { resume("Pausing for " + ptime); }, ptime); }; } // The runner drives the generator to completion function run(generatorFunction) { var it = generatorFunction(); function step(value) { var result = it.next(value); if (result.done) return; // result.value is the function returned by Timedelay result.value(step); } step(); } run(Messages);
Три точки правят това да работи. Първо, Timedelay вече не приема директно обратно извикване; връща функция, която изпълняващият може да извика, връщайки контрола обратно, когато таймерът се задейства. Второ, it.next(value) подаде предишния резултат към паузирания генератор, което позволява yield да се появи от дясната страна на заданието. Трето, бегачът спира веднага щом result.done става истина, така че рекурсията завършва естествено. Този шаблон на бегач е точно това, което библиотеки като co предоставят, и това е механизмът, който async/await по-късно абсорбирани в самия език.
Generators срещу async/await: Кое да използвате
Generators бяха стандартният начин за писане на плосък асинхронен код преди async/await съществуваше и двете споделят една и съща основна идея за паузиране на функция по средата. Node поддържа async/await първоначално от версия 7.6, което променя препоръката за всеки, който започва нова работа днес.
| Аспект | Generators | асинхронен/изчакване |
|---|---|---|
| Търси се бегач | Да, написано от вас или от библиотека | Не, вградено е в езика |
| Пауза на ключовата дума | yield |
await |
| Обработка на грешки | Бегачът трябва да препраща грешки | Обикновен опит и улов |
| Може да се направи пауза по средата | Да, и може да се възобнови избирателно | Не, работи до завършване |
| Най - доброто за | Мързеливи последователности и безкрайни потоци | Обикновена асинхронна работа |
употреба async/await за асинхронен поток в нов код, тъй като премахва runner-а и възстановява обикновените try и catch. Запазете генераторите за това, което само те правят добре: лениво генериране на стойности, итериране на големи или безкрайни последователности и всеки случай, в който потребителят решава точно кога се изчислява следващата стойност.
Общ Generator Грешки и как да ги поправим
Повечето проблеми с генераторите идват от това, че забравяме, че функцията е инертна, докато нещо не я извика. next(), или от малки синтактични грешки около звездичката. Всеки симптом по-долу посочва причината и решението си.
- Нищо не се случва, когато генераторът се извика: Извикването му връща итератор, а не резултат.
next()или го предайте на бегач. - Синтактична грешка при добива: звездичката липсва.
yieldе валидно само вътре вfunction*. - Стойността от next() пристига неопределена: първи
next()извикването не може да достави стойност, защото генераторът не е достигналyieldвсе още. Изпращайте данни от второто повикване нататък. - Безкраен цикъл в бегач:
result.doneникога не се проверява, така чеnext()продължава да се извиква и след края. Винаги връщай, когато е готово, е true. - Грешките изчезват тихо: Отхвърлянето в асинхронната стъпка никога не се връща обратно в генератора. Препраща се с
it.throw(err)така че try and catch, написан вътре в генератора, действително да може да го види и обработи.




