Обработка асинхронной природы Node.js: пример проекта

  1. Какая разница?
  2. Ад обратного вызова:
  3. Почему не хорошо иметь несколько обратных вызовов?
  4. Какое решение?
  5. Введение в Async.js
  6. Пример проекта:
  7. Откуда мы можем получить данные фильмов?
  8. Контроль потока:
  9. Названия фильмов?
  10. Дизайн базы данных.
  11. Package.json
  12. Server.js
  13. Запуск программы
  14. Объяснение кода
  15. Заключение

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

Асинхронный большой с точки зрения производительности, использования ресурсов и пропускной способности системы. Но есть некоторые недостатки:

  1. Устаревшему программисту очень сложно перейти на Async.
  2. Управлять потоком управления действительно больно.
  3. Обратные вызовы грязные.

Если вы функционально-ориентированный программист, вам будет немного сложно понять асинхронное программирование. Однако, если вы знакомы с многопоточностью в Java, то это похоже на это.

Node.js использует ECMAScript в качестве основного языка, поэтому он принимает все аспекты ECMAScript-подобных генераторов, волокон, асинхронной природы кода и т. Д. Рассмотрим следующий код, который по своей природе блокирован.

var fs = require ("fs"); fs.readFileSync ('abc.txt', function (err, data) {if (! err) {console.log (data);}}); console.log («что-то еще»);

Теперь рассмотрим следующий асинхронный код:

var fs = require ("fs"); fs.readFile ('abc.txt', function (err, data) {if (! err) {console.log (data);}}); console.log («что-то еще»);

Какая разница?

В первом модуле программа ожидала чтения файла. Он не пойдет дальше до завершения операции чтения, которая является примером блокировки кода. Но в идеале мы должны продолжить работу, пока программа читает файл, и как только это будет сделано, мы должны вернуться и обработать это. Это то, что происходит во втором коде.

Во втором коде программа не ждет, поэтому сначала вы видите консоль, а содержимое файла позже. Однако управление потоком программ такого рода обойдется вам немного больше времени и работы. Прежде чем двигаться дальше, спросите себя: как вы узнаете, было ли выполнено чтение файла?

Есть много способов сделать это, но одним из популярных способов является использование Callback, которое похоже на систему уведомлений в асинхронном программировании. Как следует из названия, он уведомит вас, когда задача Async будет завершена. Но подождите, мы еще не решили головоломку!

Наличие обратных вызовов в вашем коде не означает, что вы можете контролировать асинхронную природу JavaScript. Большинство программистов делают эту ошибку и попадают в ад обратного вызова.

Ад обратного вызова:

Ад обратного вызова:

Наличие серии обратных вызовов после обратных вызовов известно как «ад обратного вызова» в зоне JavaScript. В этом примере один обратный вызов останавливает выполнение кода, ожидает данные и затем передает их следующей функции, которая снова имеет обратные вызовы и переводит циклы событий в приостановленное состояние до завершения обработки, и так далее.

Это выглядит примерно так:

function readFile (filename, callback) {fs.readFile (имя файла, функция (err, data) if (! err) {processData (data, function (res)) {printData (res);}}}); }

Почему не хорошо иметь несколько обратных вызовов?

Когда вы используете обратные вызовы, вы в конечном итоге останавливаете поток выполнения всей вашей программы. Наличие меньшего количества обратных вызовов не является проблемой, но если у вас слишком много обратных вызовов, ваша программа будет в большей степени в состоянии ожидания, чем в состоянии выполнения. Вы действительно не хотите этого делать, потому что, как говорит Google, «не представляйте это, если это не быстро».

Какое решение?

Чтобы избежать ада обратного вызова, вы должны понимать структуру выполнения программы таким образом, чтобы ваша программа использовала ресурсы и работала быстро. Node.js имеет множество модулей для управления потоком программы, и один из самых популярных - Async.js ,

Прежде чем перейти к Async.js, давайте немного поговорим о прототипировании в Node.js. Как вы, возможно, знаете, объектно-ориентированные функции доступны в JavaScript с использованием шаблона проектирования Prototype. Шаблон дизайна прототипа также называется механизмом «скрытого класса».

При создании прототипов мы создаем классы и функции внутри класса и создаем его экземпляры с использованием объектов, как мы это делаем в C ++ или Java, но самое большое отличие состоит в том, что в JavaScript все является объектом.

Введение в Async.js

Async - это служебный модуль, который предоставляет простые мощные функции для работы с асинхронным JavaScript. Функции категоризированы в коллекции и управляют потоком вместе с утилитой.

async.map (['file1', 'file2', 'file3'], fs.stat, function (err, results) {// results теперь является массивом статистики для каждого файла}); async.filter (['file1', 'file2', 'file3'], fs.exists, function (results) {// результаты теперь равны массиву существующих файлов}); async.parallel ([function () {...}, function () {...}], обратный вызов); async.series ([function () {...}, function () {...}]);

Используя Async.js, вы можете проектировать параллельный / последовательный / пакетный поток очень легко и эффективно. Я разработаю программу, чтобы продемонстрировать то же самое.

Пример проекта:

Мы собираемся спроектировать дампер базы данных. Database dumper - это программа, которую парни из ETL используют для помещения большого количества данных в базу данных для разработчиков приложений. В нашей сцене, поскольку у нас нет никакого рабочего ETL-проекта, мы попытаемся вывести одну таблицу, которая содержит много данных Movie.

Откуда мы можем получить данные фильмов?

Вы все слышали о OMDB , самая большая коллекция фильмов всех времен. Ну, есть одна служба под названием OMDBAPI, которая предоставляет API для извлечения информации о фильмах в формате JSON. Это то, что нам нужно.

Контроль потока:

Наш поток управления будет таким:

  1. Получить название фильма из файла.
  2. Позвоните в OMDB, чтобы получить информацию о фильме.
  3. Если найдено, сбросьте в базу данных.
  4. Если нет, то перейдите к следующему фильму и повторите с шага 1.

Для этого мы будем использовать Async.waterfall () для управления потоком.

Названия фильмов?

Я скомпилировал и поместил название 3000 фильмов в файл структуры JSON для вас! Да, вы можете получить это от Github тоже.

Это похоже на этот формат:

[{"name": "40-летняя девственница"}, ...]

Дизайн базы данных.

Создайте базу данных с именем «MoviesData» и выполните этот запрос в разделе SQL.

CREATE TABLE `MoviesData`` movie` (`id` INT NOT NULL AUTO_INCREMENT,` Name` VARCHAR (50) NOT NULL, `ReleaseDate` VARCHAR (20) NOT NULL,` Year` VARCHAR (10) NOT NULL, `Cast `VARCHAR (50) NOT NULL,` Plot` VARCHAR (50) NOT NULL, `Genre` VARCHAR (50) NOT NULL,` Rated` VARCHAR (20) NOT NULL, `RunTime` VARCHAR (20) NOT NULL,` Poster `VARCHAR (20) NOT NULL,` Country` VARCHAR (20) NOT NULL, `Language` VARCHAR (20) NOT NULL,` Type` VARCHAR (20) NOT NULL, ПЕРВИЧНЫЙ КЛЮЧ (`id`)) ENGINE = InnoDB;

Убедитесь, что эта таблица создана.

Package.json

{"name": "sadique", "version": "0.0.1", "dependencies": {"async": "~ 0.9.0", "mysql": "^ 2.7.0"}}

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

установка npm

команда.

Server.js

var async = require ("async"); var http = require ("http"); var movies = require ("./ inputJSON.json"); var mysql = require ("mysql"); var movieArray = []; вар бассейн; функция processDB () {var self = this; pool = mysql.createPool ({connectionLimit: 100, хост: 'localhost', пользователь: 'root', пароль: '', база данных: 'MoviesData', debug: false}); self.collectMovies (); } processDB.prototype.collectMovies = function () {var self = this; for (var i = 0; i <movies.length; i ++) {movieArray.push (movies [i] .name); } async.eachSeries (movieArray, self.processMovie, function () {console.log ("Я закончил");}); } processDB.prototype.processMovie = function (movieName, callback) {var self = this; async.waterfall ([function (callback) {var response = ""; movieName = movieName.split ('') .join ('+'); http.get ("http://www.omdbapi.com/?t = "+ movieName +" & y = & plot = short & r = json ", function (res) {res.on ('data', function (chunk) {response + = chunk;}); res.on ('end', function ( ) {if (typeof response === "string") {response = JSON.parse (response); if (response.Response === 'False') {console.log ("Movie not found"); обратный вызов (true );} else {callback (null, response, movieName);}} else {callback (true);}}); res.on ('error', function () {console.log ("Некоторая ошибка, я думаю") ; callback (true);});});}, функция (MovieResponse, Movie, callback) {var SQLquery = 'ВСТАВИТЬ в ?? (??, ??, ??, ??, ??, ??, ??, ??, ??, ??, ??, ??) ЗНАЧЕНИЯ '+' (?,?,?,?,?,?,?,?,?,?,?,?) '; Var insertts = ["movie", "Name", "ReleaseDate", "Year", "Cast", "Plot", "Genre", "Rated", "RunTime", "Poster", "Country", "Language" , "Тип", MovieResponse.Title, MovieResponse.Released, MovieResponse.Year, MovieResponse.Actors, MovieResponse.Plot, MovieResponse.Genre, MovieResponse. imdbRating, MovieResponse.Runtime, MovieResponse.Poster, MovieResponse.Country, MovieResponse.Language, MovieResponse.Type]; SQLquery = mysql.format (SQLquery, вставки); pool.getConnection (function (err, connection) {if (err) {self.stop (err); return;} else {connection.query (SQLquery, функция (err, row)) {connection.release (); if (err ) {console.error ('ошибка при выполнении запроса', ошибка);} else {console.log ("Вставленные строки в БД");}}); callback ();}}); }], function () {callback (); }); } processDB.prototype.stop = function (err) {console.log ("ВЫПУСК С MYSQL \ n" + err); process.exit (1); } new processDB ();

Запуск программы

Давайте запустим код. Откройте свой терминал, переключитесь на каталог проекта и запустите

узел Server.js

запустить программу.

Так как мы запускаем это последовательно, то для 3000 фильмов потребуется около 7-8 минут, чтобы выгрузить базу данных.
Так как мы запускаем это последовательно, то для 3000 фильмов потребуется около 7-8 минут, чтобы выгрузить базу данных

Если вы хотите попытаться выполнить это параллельно, API OMDB заблокирует ваш IP, просто чтобы избежать атаки DOS из-за получения большого количества HTTP-запросов за одну временную метку. Вот скриншот, я пробовал Если вы хотите попытаться выполнить это параллельно, API OMDB заблокирует ваш IP, просто чтобы избежать атаки DOS из-за получения большого количества HTTP-запросов за одну временную метку

Вот как это будет работать в серии. Он выберет название фильма из файла по одному и вызовет функцию processMovie. Эта функция вызывает API-интерфейс OMDB, а затем помещает его в вашу БД и возвращает обратный вызов вызываемой стороне для предоставления имени следующего фильма.

Тот же процесс будет повторяться, пока не достигнет конца файла.

В сценарии, в котором вы получите сообщение об ошибке, например «Фильм не найден в OMDB» или «ответ пустой или пустая программа», программа просто пропустит это название фильма и не добавит его в базу данных.

Объяснение кода

Чтобы понять код, я разделил его на модули и использовал прототипы для структурирования программы.

Наш основной класс - processDB (), который также является скрытым классом, в этом классе мы создадим пул MySQL и вызовем следующую функцию.

В функции collectMovies () мы будем читать названия фильмов из файла и сохранять их в массиве, который не находится в объектном формате.

Мы используем коллекцию async.eachSeries () и передаем в нее весь массив, эта функция достаточно умен, чтобы вовремя выбрать один массив из массива, и мы вызываем функцию processMovie ().
processMovie () - это сердце этой программы. Здесь, поскольку у нас есть две операции, мы будем использовать коллекцию waterfall (). Что он делает, так это предоставляет данные для следующей функции, которая нам и нужна.

В async.waterfall () у нас есть две функции. Первый вызовет API OMDB, а второй добавит информацию в базу данных.

Мы использовали сценарии обработки ошибок в случае сбоя вызова API. В функции SQL мы создаем запрос и вызываем MySQL для этого и соответственно предоставляем обратную связь.

Заключение

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

Какая разница?
Какое решение?
Прежде чем двигаться дальше, спросите себя: как вы узнаете, было ли выполнено чтение файла?
Какое решение?
Откуда мы можем получить данные фильмов?
Названия фильмов?
Com/?
Log ("Некоторая ошибка, я думаю") ; callback (true);});});}, функция (MovieResponse, Movie, callback) {var SQLquery = 'ВСТАВИТЬ в ?