В Node.js почти все методы библиотек, работающих с внешними данными, выполняются асинхронно. Результат возвращается либо в виде событий, либо в виде вызова указанного колбека. Разработчик тут же не задумываясь использует анонимную функцию в качестве такого колбека, а затем ещё одну и ещё… В результате, достаточно сложный участок кода оказывается ещё менее читаем.
Вот несколько советов, как улучшить читаемость такого кода.
Декомпозиция
Самым очевидным решением будет использовать в качестве колбеков не анонимные функции, а именованные. Да, простая декомпозиция может значительно повысить читаемость кода.
Возможно, в именованные функции стоит вынести только бизнесс-логику, а обработку ошибок от предыдущей функции оставить в анонимном колбеке.
Step
Когда столкнулся с тем, что основная часть бизнесс-логики у меня оказывалась глубоко закопана в «ифах» и колбеках, то первой, что попалось мне на глаза, оказалась библиотека step.
Она оказалась чрезвычайно проста в использовании и дружелюбна к другим библиотекам, использующим принцип «первым аргументом колбека всегда идёт объект ошибки».
var step = require("step");
function findRecentPostsOfActiveUsers(callback) {
step(
// получаем список активных пользователей
function () {
var nextStep = this;
User.find({active: true}, nextStep);
},
// получаем последнюю публикацию каждого из ранее найденных пользователей
function (err, users) {
if (err) throw err;
var group = this.group();
users.forEach(function (user) {
Post.findOne({userId: user.id})
.sort({date: -1})
.exec(group());
});
},
// на последнем шаге передаем найденые публикации в колбек
// function (err, posts) { … }
callback
);
}
Все выбрасываемые исключения отлавливаются и передаются в качестве ошибки следующему «шагу».
Внутри каждого шага можно организовать очередь или параллельное исполнение других асинхронных функций.
Async
Библиотека async во многом аналогична step
, в части последовательного выполнения указанных функций и передачи возвращаемого результата на следующий шаг. Однако, помимо этого, там реализована масса других полезных техник для управления порядком выполнения кода.
When
Отдельно хочу отметить библиотеку when
, в которой реализован немного другой подход, чем в step
и async
. Управление последовательностью выполнения кода осуществляются на базе отложенных объектов.
Так как отложенный объект может только один раз изменить своё состояние, то переход с шага на шаг можно осуществлять, например, по таймауту не боясь, что колбеки потом будут будут вызваны несколько раз.
В примерах и документации подробно описаны аспекты применения этой библиотеки.
Замечание
Описанные библиотеки можно и нужно применять не только на сервере, но и в браузере.
Коментарии к заметке
Я не разобрался с библиотекой Step при использовании Github API — не смог понять как передать в следующий шаг содержимое ответа с сервера:
Судя по твоему куску кода, это нужно сделать так:
На каждом шаге
this
— это колбек для вызова следующего шага. Его сразу и передаёшь в асинхронную функцию. Чтобы случайно не потерять это значение, сохраним его в некой переменной. На следующем шаге параметрgist
будет соответствовать res, который вернул.get()
.Спасибо, это решило проблему.
может быть вам понравиться и поможет http://blog.jcoglan.com/2013/03/30/callbacks-are-imperative-promises-are-functional-nodes-biggest-missed-opportunity/