Статьи

Генераторы JavaScript и предотвращение обратного вызова ад

Node.js — одна из лучших технологий для создания интенсивных веб-приложений ввода / вывода. Однопоточная природа JavaScript обеспечивает много преимуществ по сравнению с моделью исполнения на основе потоков, которую можно найти в других языках программирования для этого класса приложений. Но эти преимущества имеют свою цену. Написание больших Node-приложений может быть затруднено из-за ада обратного вызова и асинхронной обработки ошибок.

Есть много решений для преодоления этих проблем. Волокна и Обещания — два из них. Некоторые разработчики предпочитают использовать эти решения, но все сводится к личным предпочтениям. В еще не выпущенной версии ECMAScript версии 6 также представлены генераторы в качестве решения проблемы обратного вызова ада. В этой статье вы познакомитесь с генераторами и узнаете, как их можно использовать для решения проблем, упомянутых выше.

Предпосылки

Как я уже говорил, генераторы — это новая функция в JavaScript. Текущая стабильная версия Node (0.10.x) не включает генераторы. Итак, нам нужно установить текущий нестабильный выпуск 0.11.x , чтобы использовать генераторы. После того, как вы установили 0.11.x, вы можете включить генераторы, передав флаг --harmony-generators Node, как показано ниже.

node --harmony-generators <filename.js>

Генераторы 101

Проще говоря, генераторы — это тип функции (обратите внимание на * в следующем примере кода), которая действует как итератор. Генераторы могут содержать любой допустимый код JavaScript. Давайте напишем наш первый генератор (показано ниже).

 function* HelloGen() { yield 100; yield 400; } var gen = HelloGen(); console.log(gen.next()); // {value: 100, done: false} console.log(gen.next()); // {value: 400, done: false} console.log(gen.next()); // {value: undefined, done: true} 

yield — это специальное ключевое слово, которое генерирует новый элемент из генератора. Мы можем использовать next() чтобы получить значения из генератора. Как только мы достигнем конца итератора, возвращаемый объект будет содержать done: true . Может быть yield любой тип данных, включая функции, числа, массивы и объекты.

Значения также могут быть переданы в генераторы, как показано ниже.

 function* HelloGen2() { var a = yield 100; var b = yield a + 100; console.log(b); } var gen2 = HelloGen2(); console.log(gen2.next()); // {value: 100, done: false} console.log(gen2.next(500)); // {value: 600, done: false} console.log(gen2.next(1000)); // {value: undefined, done: true} // prints 1000 

Предотвращение обратного вызова ада

Итак, как можно использовать генераторы, чтобы избежать ада обратного вызова? Во-первых, вам нужно понять простую технику, которую мы будем активно использовать с генераторами для написания кода без обратных вызовов.

Понимание Thunks

Thunk — это частично оцененная функция, которая принимает один обратный вызов в качестве аргумента. Внутри генераторов мы будем отдавать должное написанию программ без обратных вызовов. Простой звук показан ниже.

 function(callback) { fs.readFile('myfile.md', 'utf8', callback); } 

Thunks также можно создавать динамически, как показано ниже.

 function readFile(filename) { return function(callback) { fs.readFile(filename, 'utf8', callback); }; } 

Используя co

co — это хороший модуль, который помогает использовать thunks и генераторы вместе для создания приложений Node.js без обратных вызовов. Я покажу вам, как это работает внутри позже. А пока давайте попробуем co , который можно установить с помощью команды npm install co . Простое приложение, которое использует co и readFile() thunk из предыдущего примера, показано ниже.

 var co = require('co'); co(function* () { var file1 = yield readFile('file1.md'); var file2 = yield readFile('file2.md'); console.log(file1); console.log(file2); })(); 

Как видите, мы больше не используем обратные вызовы. Это дает нам простой способ легко создавать большие модульные приложения Node.

Как работает внутренне

Вы можете быть удивлены, как компания работает внутри. Вот как это работает, его магия.

  • Сначала он вызывает next(null) и получает thunk.
  • Затем он оценивает Thunk и сохраняет результат.
  • Затем он вызывает next(savedResult) .
  • Повторяйте эти шаги, пока next() вернет {done: true} .

Если вы предпочитаете пример кода, вот минимальная версия co написанная, чтобы показать вам, как он работает внутри. co более сложный, чем этот, так как он лучше обрабатывает ошибки и поддерживает обещания.

 function co(generator) { var gen = generator(); function nextItem(err, result) { var item = gen.next(result); if (!item.done) { item.value(nextItem); } } nextItem(); } 

Модули, которые можно использовать вместе с

co может использоваться с любым модулем, который использует thunks. К сожалению, в настоящее время не так много модулей, которые используют thunks. Вы можете увидеть полный список поддерживаемых здесь . С помощью таких простых утилит, как thu и thunkify , вы можете обернуть любой модуль Node как thunk для использования с co .

Вывод

Генераторы довольно новые и, как правило, недоступны. Тем не менее, сообщество Node, похоже, проявляет большой интерес. Одним из лучших примеров является выпуск Коа . Это дружественный к генератору клон Экспресса, созданный той же командой, что и Экспресс. Я уверен, что со временем будет увеличена поддержка генераторов от сообщества.