4

我正在使用分布在 Ember.js 中的 RSVP 库,我正在尝试找出在承诺中报告致命错误的正确模式——特别是我想告知一些几乎可以肯定是由于编程错误导致的结果api 滥用,我想以大声的方式来做。一般来说,我是 Promise 和 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 返回之前将这样的默认错误处理程序附加到promise 是否被认为是一种合理的做法?这将允许我在调用者执行一些不可能的操作时强制执行堆栈跟踪——即使堆栈跟踪会有点奇怪,它可以让事情更容易开始......

尽管我将示例承诺返回函数称为“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)

我认为这样做是在 我的异步函数调用本身引发异常的任何时候强制从某个地方抛出异常——这样的异常几乎肯定会在异步调用的参数验证阶段引发,这是最常见的这将是我的应用程序代码传入一些没有任何意义的东西的结果——我想尽快找出答案。在这种情况下,这似乎是一种有助于调试应用程序代码中使用的承诺的合理模式吗?

4

2 回答 2

2

新答案——

在这个与 ember 核心开发人员的视频小组讨论中,开发人员在某一时刻都分享了一个调试技巧:

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

Tom Dale 专门解决了 Promise 中吞下异常的问题,并建议使用新的 Ember.RSVP.onerror 功能来调试 Promise 中的错误,否则这些错误会因为没有附加拒绝处理程序而未被报告。

我认为这是对我的问题的正确答案——尽管我还不知道如何使用 RSVP.onerror 回调(或者 ember 发布可用的回调)......

于 2013-08-01T00:25:17.363 回答
0

好问题!

setTimeout您可以在开发环境中逃脱。但是对于您记录这些错误以进行报告的生产,您将希望避免setTimeout,因为它是不确定的。您最终可能会看到并非真正准确的错误,而是由于setTimeout触发的某些顺序而发生的。

您可以通过将您的检查移动到then承诺的第一个,然后返回thenable而不是直接延迟来做到这一点。

我更喜欢使用Ember.Deferred而不是Ember.RSVP.Promise. Deferred是一个RSVP具有更好 API 的层。它避免了将整个内容嵌套在回调中Ember.RSVP.PromiseEmber.Deferred提供resolvereject作为方法。

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;
},

在 Promise/API 中的任何时候抛出错误都会自动拒绝 Promise。所以你不需要捕捉和重新抛出任何东西。

这允许 3 种不同类型的响应。

带有响应数据的成功回调。

 successCallback({data: 'foo'});

successCallback 响应错误。

 successCallback({isError: true, errorMessage: 'Response data is wrong'});

带有消息的错误回调。

 errorCallback('Incorrect use of API');

请参阅此jsbin以尝试此操作。

如果您调用的 API 使用承诺而不是回调,您可以稍微清理一下。然后,您可以将thens.

于 2013-07-26T05:56:35.023 回答