Что такое антипаттерн явной конструкции обещания и как его избежать?
Я писал код, который выглядит примерно так:
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
антипаттерн конструктора » соответственно. Что плохого в этом коде и почему это называется антипаттерном ?
Ответы
Отложили антипаттерны (теперь явно-строительная антипаттерн) придуман Esailija являются общими антипаттернами людей , которые являются новыми для обещания сделать, я сделал это само , когда я впервые использовал слово. Проблема с приведенным выше кодом заключается в том, что он не использует факт цепочки обещаний.
Обещания могут быть связаны друг с другом, .then
а вы можете возвращать обещания напрямую. Ваш код getStuffDone
можно переписать как:
function getStuffDone(param){
return myPromiseFn(param+1); // much nicer, right?
}
Обещания направлены на то, чтобы сделать асинхронный код более читабельным и вести себя как синхронный код, не скрывая этого факта. Обещания представляют собой абстракцию над значением однократной операции, они абстрагируют понятие оператора или выражения на языке программирования.
Вы должны использовать отложенные объекты только тогда, когда вы конвертируете API в обещания и не можете делать это автоматически, или когда вы пишете функции агрегирования, которые проще выразить таким образом.
Цитата Есаилия:
Это самый распространенный антипаттерн. В это легко попасть, если вы не понимаете обещаний и думаете о них как о прославленных эмиттерах событий или утилите обратного вызова. Подведем итоги: обещания заключаются в том, чтобы сохранить в асинхронном коде большинство утраченных свойств синхронного кода, таких как плоский отступ и один канал исключения.
Что с этим не так?
Но шаблон работает!
Повезло тебе. К сожалению, это, вероятно, не так, поскольку вы, вероятно, забыли какой-то крайний случай. Более чем в половине случаев, которые я видел, автор забывает позаботиться об обработчике ошибок:
return new Promise(function(resolve) {
getOtherPromise().then(function(result) {
resolve(result.property.example);
});
})
Если другое обещание будет отклонено, это произойдет незамеченным, вместо того, чтобы распространяться на новое обещание (где оно будет обработано) - и новое обещание останется навсегда отложенным, что может вызвать утечки.
То же самое происходит в случае, если ваш код обратного вызова вызывает ошибку - например, когда result
нет property
и возникает исключение. Это останется без внимания, и новое обещание останется невыполненным.
Напротив, using .then()
автоматически обрабатывает оба этих сценария и отклоняет новое обещание при возникновении ошибки:
return getOtherPromise().then(function(result) {
return result.property.example;
})
Отложенный антипаттерн не только громоздок, но и подвержен ошибкам . Использование .then()
для связывания намного безопаснее.
Но я со всем справился!
В самом деле? Хороший. Однако это будет довольно подробно и обильно, особенно если вы используете библиотеку обещаний, которая поддерживает другие функции, такие как отмена или передача сообщений. Или, может быть, это произойдет в будущем, или вы хотите поменять свою библиотеку на лучшую? Вам не захочется переписывать для этого свой код.
Методы библиотек ( then
) не только изначально поддерживают все функции, но и могут иметь определенные оптимизации. Их использование, скорее всего, ускорит ваш код или, по крайней мере, позволит оптимизировать будущие версии библиотеки.
Как мне этого избежать?
Поэтому всякий раз, когда вы вручную создаете Promise
или Deferred
и уже задействованы уже существующие обещания, сначала проверьте API библиотеки . Отложенный антипаттерн часто применяется людьми, которые рассматривают обещания [только] как шаблон наблюдателя, но обещания - это больше, чем обратные вызовы : они должны быть компонуемыми. В каждой приличной библиотеке есть множество простых в использовании функций для составления обещаний всеми мыслимыми способами, заботясь обо всех низкоуровневых вещах, с которыми вы не хотите иметь дело.
Если вы обнаружили необходимость составить некоторые обещания новым способом, который не поддерживается существующей вспомогательной функцией, написание собственной функции с неизбежными отложенными значениями должно быть вашим последним вариантом. Рассмотрите возможность перехода на более функциональную библиотеку и / или сообщите об ошибке в вашей текущей библиотеке. Его сопровождающий должен уметь выводить композицию из существующих функций, реализовывать для вас новую вспомогательную функцию и / или помогать идентифицировать крайние случаи, которые необходимо обработать.