Приемлемый шаблон обещания для ошибок "LOUD"?
Я использую библиотеку RSVP, распространяемую внутри Ember.js, и я пытаюсь выяснить правильную модель для сообщения фатальных ошибок внутри обещания - особенно я хочу сообщить о чем-то, что почти наверняка является результатом программирования ошибка из-за неправильного использования api, и я хочу сделать это с помощью LOUD. Я новичок в promises и javascript в целом, надеюсь, этот вопрос имеет смысл
Вот простой пример (в coffeescript):
doAsync = (arg, callback) ->
throw new Error('you gave me a way bad arg, I fail for you!')
promiseReturningApi = (data) ->
return new Ember.RSVP.Promise (resolve, reject) ->
callback = (err, resp) ->
if err then reject(err)
else resolve(resp)
doAsync(data, callback)
Теперь позвольте сказать, что я обнаружил ошибку, что нет возможного способа восстановления, из которой произошла внутри doAsync. Я хочу, чтобы эта ошибка сообщалась даже в том случае, если вызывающий абонент забыл приложить обработчик ошибок, потому что это почти наверняка только потому что вызывающий вызывал функцию api некорректным образом
Я столкнулся с идеей использования setTimeout в обработчике отклонения, чтобы гарантировать, что ошибка повышается откуда-то, даже если вызывающий не прикрепляет обработчик ошибок к обещанию
failLoud = (err) ->
if err.isProgrammerError
setTimeout () ->
throw err
throw err
promiseReturningApi = (data) ->
promise = new Ember.RSVP.Promise (resolve, reject) ->
callback = (err, resp) ->
if(err) then reject(err)
else resolve(resp)
doAsync(data, callback)
return promise.then(null, failLoud)
Является ли разумная практика прикреплять такой обработчик ошибок по умолчанию к обещанию, прежде чем возвращать его из моего обещанияReturningApi? Это позволило бы мне заставить stacktrace, когда вызывающий делает что-то, что не может работать, даже несмотря на то, что stacktrace будет немного странным, что может сделать вещи немного легче начать с...
Несмотря на то, что я назвал пример обещания, возвращающего функцию, вызовом "api" - я должен добавить, что я не пишу код рамки - это скорее всего в приложении. Если doAsync - это реальная функция, то в моей версии реального мира она скорее всего будет поступать с внешней стороны с помощью api-а-а-а-а-а, поэтому кажется, что я буду злоупотреблять ею, пока Я узнаю об этом... Значит, я мог бы сделать шаблон похожим на это
failLoud = (err) ->
if err?.isProgrammerError
setTimeout () ->
throw err
throw err
promiseReturningApi = (data) ->
promise = new Ember.RSVP.Promise (resolve, reject) ->
callback = (err, resp) ->
if(err) reject(err)
resolve(resp)
try
doAsync(data, callback)
catch err
err.isProgrammerError = true
throw err
return promise.then(null, failLoud)
Я думаю, что это то, что вы делаете исключение, которое должно быть выбрано откуда-то в любое время, когда сам вызов вызова асинхронной функции вызывает исключение - такое исключение почти наверняка будет поднято во время фазы проверки аргумента асинхронного вызова, чаще всего будет результатом того, что код моего приложения передается во что-то, что не имеет никакого смысла, - и я хочу узнать об этом, как только смогу. Кажется ли это разумной моделью, чтобы помочь в отладке promises, используемой в коде приложения в этом контексте?
Ответы
Ответ 1
Новый ответ -
В этом обсуждении на панели управления с разработчиками ember core разработчики в один момент делятся одним отладчиком:
http://www.youtube.com/watch?v=L9OOMygo1HI
Том Дейл конкретно рассматривает проблему проглатываемых исключений внутри promises и рекомендует использовать новую функцию Ember.RSVP.onerror для отладки ошибок внутри promises, которые иначе не были бы отправлены без регистрации, потому что обработчик отклонения не был прикреплен.
Я думаю, что это правильный ответ на мой вопрос - хотя я еще не знаю, как использовать обратный вызов RSVP.onerror(или в котором ember освобождает его)...
Ответ 2
Хороший вопрос!
Вы можете уйти с setTimeout
в среде dev. Но для производства, где вы регистрируете эти ошибки для отчетности, например, вы захотите избежать setTimeout
, поскольку он не является детерминированным. Вы можете в конечном итоге увидеть ошибки, которые не очень точны, но произошли из-за некоторого порядка, в котором срабатывает setTimeout
.
Вы можете сделать это, перенести свои чеки на первый then
обещания, а затем вернуть это thenable
вместо того, чтобы отложить напрямую.
Я предпочитаю использовать Ember.Deferred
вместо Ember.RSVP.Promise
. Deferred
- это слой поверх RSVP
, который имеет более удобный API. Он избегает необходимости вложенности всего объекта в обратный вызов Ember.RSVP.Promise
. Ember.Deferred
предоставляет resolve
и reject
как методы.
model: function(params) {
var promise = Ember.Deferred.create();
var successCallback = function(resp) {
promise.resolve(resp);
};
var errorCallback = function(err) {
promise.reject(err);
};
var thenable = promise.then(function(resp) {
if (resp.isError) {
throw new Error(resp.errorMessage);
} else {
return resp;
}
});
// some api that takes a callback
this.callApi(params, successCallback, errorCallback);
return thenable;
},
Бросание ошибки в любой момент обещания /API автоматически отклонит обещание. Поэтому вам не нужно ловить и ниспровергать что-либо.
Это позволяет использовать 3 разных типа ответов.
successCallback с данными ответа.
successCallback({data: 'foo'});
successCallback с ошибкой в ответе.
successCallback({isError: true, errorMessage: 'Response data is wrong'});
errorCallback с сообщением.
errorCallback('Incorrect use of API');
Посмотрите jsbin, чтобы попробовать это.
Вы можете очистить это немного, если API, который вы вызываете, использует promises вместо обратных вызовов. Затем вы можете просто связать thens
.