Боремся с большой вложенностью анонимных колбеков

В 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. Управление последовательностью выполнения кода осуществляются на базе отложенных объектов.

Так как отложенный объект может только один раз изменить своё состояние, то переход с шага на шаг можно осуществлять, например, по таймауту не боясь, что колбеки потом будут будут вызваны несколько раз.

В примерах и документации подробно описаны аспекты применения этой библиотеки.

Замечание

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