Приемлемый шаблон обещания для ошибок "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.