Первое, что вы обнаружите при поиске промисов angular и $q, — это отложенный (анти)паттерн, который мы все использовали и до сих пор используем в наших приложениях. Это происходит из-за нашего неправильного понимания асинхронного кода, а затем мы пытаемся писать код асинхронно с синхронными шаблонами, поэтому мы создаем объект отложенного выполнения, который вообще не нужен. Я также виню в этом Angular, так как это находится на их странице документации $q, где отложенный API объясняется без уведомления о лучших практиках.

Вот пример из нашего собственного кода:

В этом примере мы создаем объект отсрочки, а затем вызываем метод отклонения, если получаем ошибку.

Чтобы избавиться от этого анти-шаблона, мы могли бы написать такой код:

Таким образом мы решили даже два антипаттерна. Во-первых, нужно избавиться от объекта отсрочки, так как service.getStatus уже является промисом, и мы можем распространять результаты вверх по цепочке с помощью инструкции return. Второй — заменить функции успеха и ошибки на catch, которые предлагает q или Bluebird. Таким образом, мы получаем более удобочитаемую функцию, и никто не задается вопросом, какая функция что означает (then =› catch =› finally намного понятнее).

Кроме того, отлов ошибок в сервисе не рекомендуется, поэтому код можно было бы упростить:

Таким образом, отлов ошибок будет выполняться в контроллере, и мы сохраним сервис чистым и независимым от форматирования выходных данных конечного пользователя (в этом случае событие, возвращающее ответные данные, не обязательно, но мы можем сделать вид, что сервис выполняет некоторые манипуляции с данными). Чистый сервис можно повторно использовать. С другой стороны, мы не потеряем никаких ошибок или отказов.

Вот более сложный пример этого шаблона:

Вы заметите, что есть несколько промисов, и их результаты распространяются по всей цепочке до вызывающей стороны.

ПРИМЕЧАНИЕ. Пример выше — это метод класса, написанный на es6.

Итак, когда использовать отложенный объект?

Ну, практически никогда, когда дело доходит до работы с объектами, которые можно использовать, но defer можно использовать для преобразования некоторого обратного вызова в функцию, которую можно использовать.

Вот пример, в котором мы создаем объект defer для обещания загрузки изображения:

То же самое можно сделать с помощью конструктора $q:

Спасибо Петке Антонову и его замечательной Библиотеке обещаний Bluebird.