Что такое явное обещание строительства антипаттерн и как его избежать?
Я писал код, который делает что-то похожее:
function getStuffDone(param) { | function getStuffDone(param) {
var d = Q.defer(); /* or $q.defer */ | return new Promise(function(resolve, reject) {
// or = new $.Deferred() etc. | // using a promise constructor
myPromiseFn(param+1) | myPromiseFn(param+1)
.then(function(val) { /* or .done */ | .then(function(val) {
d.resolve(val); | resolve(val);
}).catch(function(err) { /* .fail */ | }).catch(function(err) {
d.reject(err); | reject(err);
}); | });
return d.promise; /* or promise() */ | });
} | }
Кто-то сказал мне, что это называется " отсроченный антипаттерн " или "противник- конструктор Promise
" соответственно, что плохого в этом коде и почему это называется антипаттерн?
Ответы
Ответ 1
отсроченный антипаттерн (теперь явный антиконтурный шаблон), придуманный Esailija - это обычные люди с анти-шаблонами, которые новичок в promises make, я сделал это сам, когда впервые использовал promises. Проблема с приведенным выше кодом заключается в том, что не используется тот факт, что promises chain.
Promises может связываться с .then
и вы можете напрямую возвращать promises. Ваш код в getStuffDone
можно переписать как:
function getStuffDone(param){
return myPromiseFn(param+1); // much nicer, right?
}
Promises - все о том, как сделать асинхронный код более читаемым и вести себя как синхронный код, не скрывая этого факта. promises представляют абстракцию по значению одноразовой операции, они абстрагируют понятие оператора или выражения на языке программирования.
Вы должны использовать только отложенные объекты, когда вы конвертируете API в promises и не можете делать это автоматически, или когда вы пишете функции агрегации, которые легче выразить таким образом.
Цитата Esailija:
Это самый распространенный анти-шаблон. Легко впасть в это, когда вы действительно не понимаете promises и думаете о них как о прославленных эмитентах событий или утилите обратного вызова. Пусть recap: promises о создании асинхронного кода сохраняют большинство потерянных свойств синхронного кода, таких как плоский отступ и один канал исключения.
Ответ 2
Что с ним не так?
Но шаблон работает!
Счастливчик. К сожалению, это, вероятно, не так, как вы, вероятно, забыли какой-то край. В более чем половине случаев, которые я видел, автор забыл позаботиться о обработчике ошибок:
return new Promise(function(resolve) {
getOtherPromise().then(function(result) {
resolve(result.property.example);
});
})
Если другое обещание будет отвергнуто, это произойдет незамеченным, а не будет распространено на новое обещание (где оно будет обработано) - и новое обещание останется навечно ожидающим, что может вызвать утечки.
То же самое происходит в случае, когда ваш код обратного вызова вызывает ошибку - например. когда result
не имеет property
и генерируется исключение. Это будет необработанным и оставить новое обещание нерешенным.
В отличие от этого, использование .then()
автоматически выполняет оба этих сценария и отклоняет новое обещание при возникновении ошибки:
return getOtherPromise().then(function(result) {
return result.property.example;
})
Отложенный антипаттерн не только громоздкий, но и подверженный ошибкам. Использование .then()
для цепочки намного безопаснее.
Но я справился со всем!
Действительно? Хорошо. Однако это будет довольно подробно и обидно, особенно если вы используете библиотеку обещаний, которая поддерживает другие функции, такие как аннулирование или передача сообщений. Или, может быть, это произойдет в будущем, или вы захотите обменять свою библиотеку на лучшую? Вы не захотите переписать свой код для этого.
Методы библиотек (then
) не только поддерживают все функции, но также могут иметь определенные оптимизации. Использование их, скорее всего, сделает ваш код быстрее или, по крайней мере, позволит оптимизировать будущие версии библиотеки.
Как этого избежать?
Поэтому всякий раз, когда вы вручную вручную создаете Promise
или Deferred
и уже существующие promises, сначала проверяйте API библиотеки. Отсроченный антипаттерн часто применяется людьми, которые видят promises [only] как шаблон наблюдателя, но promises больше, чем обратные вызовы: они должны быть составными, Каждая достойная библиотека имеет множество простых в использовании функций для состава promises в любой мыслимой манере, заботясь обо всех низкоуровневых вещах, с которыми вы не хотите иметь дело.
Если вы обнаружили необходимость компоновки некоторого promises по-новому, который не поддерживается существующей вспомогательной функцией, ваша последняя функция должна быть вашей последней опцией. Рассмотрите возможность переключения на более функциональную библиотеку и/или файл с ошибкой в текущей библиотеке. Его сопровождающий должен иметь возможность выводить композицию из существующих функций, реализовывать новую вспомогательную функцию для вас и/или помогать идентифицировать граничные случаи, которые необходимо обрабатывать.