The AngularJs Promise Pattern

Или давайте наконец-то уже писать promises «человеческим» языком!

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

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

Собственно, пример ниже,

Прежде чем раздуть holy war, вы конечно можете спросить — «что же тут не верно? «, собственно «почти» все верно и даже может быть приемлемо в некоторых случаях. Как видно из кода, мы стараемся разделить обработчики, абстрактные обратные вызовы и код более менее чистый. Однако, при таком подходе использования promise, мы столкнемся с некоторыми проблемами. Собственно, давайте обсудим все по порядку.

День первый: Initial code

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

Нет проблем, мы ведь гуру программирования и за пару минут пишем примерно вот такой код,

И вуаля, код для нашей страницы готов! Все работает как и было задуманно. Я почти уверен, что большая часть разработчиков написали бы именно такой код (не лукавте, все, хотя бы один раз именно так и писали) .

День второй: Changing requirements

На утро понедельника менеджер подбегает уже с огорченным лицом и словами — «Клиенты жалуются, им не нравиться этот переход на другую страницу. Нам надо добавить вторую кнопку, которая сохраняет товар, но мы остаемся на этой странице!».

Отлично, добавим несколько новых строчек кода в наш контроллер и все будет отлично. Давайте рассмотрим, что нам надо сделать для этого,

  • добавить другой обработчик события для кнопки «обновления» (не уходя со страницы)
  • оба обработчика имеют общию логику работы — сохранение
  • только один должен перевести пользователя на другую страницу
  • оба обработчика «ошибки» имеют общию логику

Отлично, обновляем наш код,

Учитывая все обстоятельства, мы получили вполне «живой» код. У нас есть общий метод «commonUiLogic», который обновляет интерфейс в обоих случаях. И мы вызываем один метод сохранения в обоих обработчиках.

Все здорово!

День третий: More and more changing requirements

И только мы написали этот замечательный код, как снова подбегает менеджер — «Красный цвет очень яркий для нашего интерфейса, нам нужен синий. И еще надо добавить одну кнопку, которая вначале изменяет цвет на синий, а потом делает переход на другую страницу.» Ох… давайте попробуем сделать.

Вот, мы добавили так же быстро и легко еще одну кнопку! Но, мы начинаем видеть, что каждое новое добавление функционала требует всегда нового рефакторинга, что не очень удобно и не дает нам возможности повторно использовать этот код.

Должен быть другой путь

Давайте остановимся и посмотрим, можем ли мы быть немного умнее и сделать этот код более простым, удобным ?
И так, нам необходимо — три кнопки, изменение цвета фона, изменение цвета шрифта и разная маршрутизация.

Так же лучше ! Чувствуете «легкость» в коде ?

Отметим основные отличия данной организации кода. Во-первых service теперь возвращает promise на прямую. У нас больше нет встроенных (inline) функций в callback’ах. Мы построили удобно читабильные цепочки callback’ов. И конечно, сделали код более удобным для unit test.

В заключение

  • старайтесь делать «обертку» promise в сервисах. Как правило, контроллеры не должны выполнять такую логику, только использовать результат promise.
  • возвращайте в сервисах callback как параметр, это позволит сделать несколько callback
  • делайте обертки над глобальными callback «success» и «failure», это позволит упростить Ваши цепочки

И это только пример, не забывайте, о том что

  • часть логики возможно, а иногда и нужно выносить в factory
  • никогда, никогда, не пишите такие комментарии к реальному коду
  • любой код может существовать в реальной жизни, вопрос «долго ли?»