приемлемый шаблон обещания для ошибок LOUD?

Я использую библиотеку RSVP, распространяемую внутри Ember.js, и пытаюсь выяснить правильный шаблон для сообщения о фатальных ошибках внутри обещания — в частности, я хочу сообщить о чем-то, что почти наверняка является результатом ошибки программирования из-за неправильное использование API, и я хочу сделать это ГРОМКО. Я новичок в обещаниях и 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)

Считается ли разумной практикой прикреплять такой обработчик ошибок по умолчанию к обещанию перед его возвратом из моего promiseReturningApi? Это позволило бы мне форсировать трассировку стека, когда вызывающая сторона делает что-то, что не может работать - даже если трассировка стека была бы немного странной, это могло бы немного облегчить начало работы...

Несмотря на то, что я назвал функцию, возвращающую промис, вызовом 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)

Я думаю, что это принудительно вызывает исключение из где-то каждый раз, когда мой асинхронный вызов функции сам вызывает исключение - такое исключение почти наверняка будет вызвано на этапе проверки аргумента асинхронный вызов, который чаще всего является результатом того, что код моего приложения передает что-то, что не имеет никакого смысла, и я хочу узнать об этом, как только смогу. Кажется ли это разумным шаблоном для помощи в отладке обещаний, используемых в коде приложения в этом контексте?


person Ben    schedule 25.07.2013    source источник


Ответы (2)


Новый ответ --

В этом видеообсуждении с разработчиками ядра Ember в какой-то момент все разработчики делятся одним советом по отладке:

http://www.youtube.com/watch?v=L9OOMygo1HI

Том Дейл специально рассматривает проблему проглатывания исключений внутри промисов и рекомендует использовать новую функцию Ember.RSVP.onerror для отладки ошибок внутри промисов, которые в противном случае остались бы незамеченными, поскольку не был прикреплен обработчик отклонения.

Я думаю, что это правильный ответ на мой вопрос - хотя я еще не знаю, как использовать обратный вызов RSVP.onerror (или в котором ember выпускает его доступный) ...

person Ben    schedule 01.08.2013
comment
В частности, я нашел решение здесь - › stackoverflow.com/questions/17983946/ - person Craig Heneveld; 01.11.2013

Хороший вопрос!

Вы можете избежать неприятностей с setTimeout в среде разработки. Но для производства, где вы регистрируете эти ошибки, например, для отчетов, вам следует избегать 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'});

errorОбратный вызов с сообщением.

 errorCallback('Incorrect use of API');

См. этот jsbin, чтобы попробовать это.

Вы можете немного исправить это, если API, который вы вызываете, использует промисы вместо обратных вызовов. Затем вы можете просто связать thens.

person Darshan Sawardekar    schedule 26.07.2013
comment
Если я правильно все понимаю, в вашем примере this.callApi может синхронно выдавать ошибку (до выполнения чего-либо асинхронного), что приведет к сбою в самом раннем возможном месте (в вашем примере на сайте вызова функции model()). Принимая во внимание, что в моем примере сбой до возврата doAsync приводит к тому, что обещание переходит в состояние отклонения. В идеале приложение найдет эту ошибку и сообщит о ней каким-нибудь полезным способом позже, но для класса ошибок, которые я хочу сделать очень очевидными, я бы предпочел, чтобы они были взорваны как можно раньше. - person Ben; 27.07.2013
comment
Я добавил некоторую логику приложения в jsbin, который вы разместили внутри callApi..., имитируя проверку аргумента разнообразия, которая может завершиться ошибкой до того, как произойдет что-либо асинхронное. В контексте хука модели в ember у меня нет четкого представления о том, что происходит на стороне кода фреймворка, который вызывает model(), но если бы это была какая-то другая функция приложения, которая должна вызываться мой собственный код .. я думаю, что в большинстве случаев взорваться рано было бы правильно. Вот модификация jsbin, которую я сделал: jsbin.com/ecarah/2/edit - person Ben; 27.07.2013
comment
Да, выдача ошибки непосредственно в хуке model также будет считать все обещание отклоненным. На стороне ember это делает маршрутизатор в виде последовательности обещаний, beforeModel, model, afterModel и т. д. Все они должны завершиться успешно, иначе переход не удастся. - person Darshan Sawardekar; 27.07.2013
comment
Но мне все еще что-то приходит в голову по поводу этой фразы: выдача ошибки в любой точке промиса/API автоматически отклонит промис. Так что не нужно ничего ловить и перебрасывать. Я думаю, что понимаю это - и здорово, что структура ember, кажется, гарантирует, что исключения в отклоненных промисах, возвращенных из хука модели, не останутся незамеченными. Когда дело доходит до моего собственного кода, у меня нет такой уверенности, что я буду прикреплять обработчик ошибок везде, где мне нужно... ember не сможет защитить меня, если я скрою отклоненное обещание в обещании, которое я не вернуться в Эмбер... - person Ben; 27.07.2013
comment
Отклонение обещаний об ошибках является частью спецификации Promises/A+. Вы правы насчет сокрытия, о чем нужно знать. - person Darshan Sawardekar; 27.07.2013
comment
Я думаю, что этот шаблон может иметь смысл failLoud = (err) -> if App.debugMode setTimeout () -> throw err throw err Я думаю, что мне имеет смысл прикрепить этот обработчик отказа в «ориентированных на приложения» местах, где я еще не полностью представил, как приложение может работать даже в случае отклонения промиса, а затем, когда я лучше пойму, как части сочетаются друг с другом, я могу вернуться и удалить rejectionHandler или заменить его чем-то более разумным . Спасибо за советы! (редактировать: ... Вау, нельзя вставлять код в комментарии) - person Ben; 27.07.2013