Комбо-библиотека Do
При использовании асинхронных функций в Node.js программист часто сталкивается с однотипными задачами, красивое решение к которым на первый взгляд найти не очень просто. Несколько примеров:
- Ожидание нескольких событий сразу и выполнение функции по завершению всех событий (например, открытие нескольких соединений)
- Выполнение событий по цепочке, когда следующая функция использует результат работы предыдущей — это можно сделать стандартным способом, вкладывая функции друг в друга, но такой синтаксис быстро становится неуклюжим
- Применение асинхронного обработчика к массиву значений, с целью получения массива результатов
К счастью для нас, Github-пользователь creationix написал удобную библиотеку, которая умеет делать всё вышеперечисленное и кое что ещё.
Скачать Do можно с Github. Я не буду описывать установку, она не отличается от любого другого модуля для Node. Я просто рассмотрю её основные применения. Но сначала — пару слов о continuables.
Асинхронное программирование в Node.js: Ожидание нескольких событий сразу
Постановка задачи
Представим себе такую ситуацию. Нам надо сделать поиск в базе Tokyo Tyrant (или любой другой) и получить найденные значения в виде JavaScript-объектов. Проблема состоит в том, что получение объекта из Tokyo Tyrant происходит с коллбеком. Т.е., у нас на руках после поиска оказывается несколько коллбеков - по числу ожидаемых объектов:
Асинхронное программирование в Node.js: события, коллбеки, promises
Неблокирующие функции
Основная «фишка» node.js, как известно, в том что большинство функций в ней неблокирующие. Что это значит для программиста?
Операции в программе отнимают разное время в зависимости от того к чему мы обращаемся. Операции с регистрами — самые быстрые, потом идут операции с кэшами первого и второго уровней (1 и 5 наносекунд соответственно), операции с RAM (~80-90 нс), операции с жёстким диском (~14 миллисекунд). Чтобы понять масштаб, можно взглянуть вот на этот gif. Огромная колонна - обращение к жёсткому диску. Если сделать zoom in, сверху будут видны операции с памятью и кэшами. Обращение к сетевому серверу ещё дольше — когда придёт ответ, вообще непонятно.
Философия неблокирующего ввода-вывода состоит в том что функции вообще не должны ждать окончания длительных операций ввода-вывода (диск и сеть). Для этого функция либо принимает функцию, которую надо выполнить по завершении операции (callback), либо возвращает объект-эмиттер, на который опять же вешаются функции. Пример:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | // Оставление callback'а server.query('Query text', function(data) { // Сделать что нибудь с данными }); // Оставление event-эмиттера var query_status = server.query('Query text'); query_status.addListener('success', function(data){ // Сделать что нибудь с данными }); query_status.addListener('error', function(error){ // Если пришла ошибка, тоже что нибудь сделать }); |