【问题标题】:Define fallback catch for promise chain?定义承诺链的后备捕获?
【发布时间】:2017-02-05 07:03:43
【问题描述】:

我正在开发一个使用 what-wg fetch 的应用程序。我们以这种方式定义了默认的 fetch 中间件和选项:

export function fetchMiddleware(response) {
  return new Promise(resolve => {
    resolve(checkStatus(response));
  }).then(parseJSON);
}

export const fetchDefaults = {
  credentials: 'same-origin',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json'
  }
};

我们以这种方式使用我们的默认中间件/获取选项:

fetch('/api/specific/route', fetchDefaults)
  .then(fetchMiddleware)
  .then(function(data) {
    // ... Dispatch case-specific fetch outcome
    dispatch(specificRouteResponseReceived(data));
  });

我们希望为整个应用程序中的所有 fetch 用法添加一个通用的后备捕获,换句话说,如下所示:

export function fetchGenericCatch(function(error) {
  showGenericErrorFlashMessage();
})

fetch('/en/api/user/me/preferences', fetchDefaults)
  .then(fetchMiddleware)
  .then(function(data) {
    dispatch(userPreferencesReceived(data));
  })
  .catch(fetchGenericCatch);

大量代码重复。我们想要一个可以为我们做所有这些的实用函数/类,例如像这样工作的东西:

genericFetch('/api/specific/route') // bakes in fetchDefaults and fetchMiddleware and fetchGenericCatch
  .then(function(data) {
    dispatch(userPreferencesReceived(data));
  }); // gets generic failure handler for free

genericFetch('/api/specific/route') // bakes in fetchDefaults and fetchMiddleware and fetchGenericCatch
  .then(function(data) {
    dispatch(userPreferencesReceived(data));
  })
  .catch(function(error) {
    // ...
  }); // short-circuits generic error handler with case-specific error handler

主要需要注意的是,通用 catch 必须链接在 特定案例的 thens / catches 之后。

关于如何使用 whatwg-fetch / ES6 Promises 实现这一点的任何提示?

相关:

有类似的帖子,但它们似乎没有解决运行默认捕获的需求毕竟非默认thens 和catches:

10 月 14 日编辑:

可能重复:Promises and generic .catch() statements

【问题讨论】:

  • 我是否理解正确,因为您想自动添加 .catch 侦听器,即使用户在您的函数返回后附加了 .then/.catches ?这是不可能的 AFAIK。
  • 不,我想添加一个默认承诺 catch 它将处理任何未捕获的错误 --- 如果错误适用 --- 如果 catch 处理程序没有另外定义。

标签: javascript promise ecmascript-6 es6-promise


【解决方案1】:

我认为解决方案很简单:

export function genericFetch(url, promise, optionOverrides) {
  const fetchOptions = {...fetchDefaults, ...optionOverrides};
  return fetch(url, fetchOptions)
    .then(fetchMiddleware)
    .then(promise)
    .catch(function(error) {
      showGenericFlashMessage();
    });
}

不需要特殊错误处理程序的用例可以简单地以这种方式使用它:

genericFetch('/api/url', function(data) {
  dispatch(apiResponseReceived(data));
});

需要特殊捕获或更复杂链的用例可以传入完整的 Promise:

genericFetch('/api/url', function(response) {
  return new Promise(resolve, reject => {
    dispatch(apiResponseReceived(data));
  }).catch(nonGenericCaseSpecificCatch); // short-circuits default catch
});

【讨论】:

  • 这行不通,它不会使默认的catch短路
【解决方案2】:

只要错误处理程序是 DRY,使用 WET 代码并不是最糟糕的选择。

fetch(...)
...
.catch(importedFetchHandler);

它不会导致任何问题,并且符合 Bluebird 和 V8 Promise 的行为,其中存在未处理的拒绝事件以确保没有任何 Promise 未被捕获。


实现这一点的最简单方法是为fetchpromise 引入类似promise 的包装器:

function CatchyPromiseLike(originalPromise) {
  this._promise = originalPromise;

  this._catchyPromise = Promise.resolve()
  .then(() => this._promise)
  .catch((err) => {
    console.error('caught', err);
  });

  // every method but 'constructor' from Promise.prototype 
  const methods = ['then', 'catch'];

  for (const method of methods) {
    this[method] = function (...args) {
      this._promise = this._promise[method](...args);

      return this;
    }
  }
}

可以像这样使用

function catchyFetch(...args) {
  return new CatchyPromiseLike(fetch(...args));
}

像这样的 promise 有天然的局限性。

如果转换为真正的承诺,副作用将被丢弃:

Promise.resolve(catchyFetch(...)).then(() => /* won't be caught */);

而且它不能很好地与异步链配合使用(这是所有承诺的禁忌):

var promise = catchyFetch(...);

setTimeout(() => {
  promise.then(() => /* won't be caught */);
});

一个不错的选择是 Bluebird,无需在那里发明轮子,功能就是它的最爱。 Local rejection events 看起来正是我们所需要的:

// An important part here,
// only the promises used by catchyFetch should be affected
const CatchyPromise = Bluebird.getNewLibraryCopy();

CatchyPromise.onPossiblyUnhandledRejection((err) => {
  console.error('caught', err);
});

function catchyFetch(...args) {
  return CatchyPromise.resolve(fetch(...args));
}

唷,这很容易。

【讨论】:

  • 爱蓝鸟onPossiblyUnhandledRejection 解决方案。就像 JS Bin 的魅力一样。由于某些完全神秘的原因,它在我们的 babel 设置中不起作用。我们会单独解决的:)再次感谢jsbin.com/rerafucoza/edit?js,console
  • 不客气。请注意,只有最新的 Bluebird 版本支持 getNewLibraryCopy。
  • 是的,我们也在使用 3.4.6。函数调用存在等等。还是没有效果,很奇怪!尽管代码都与 JS Bin 匹配,但没有捕获到错误。尝试各种 babel 预设和插件,以了解以其他方式正确注入 bluebird。星期一的事情可能看起来更清楚了。
  • Bluebird 在非模块化环境中替换全局 Promise(可控制bluebirdjs.com/docs/api/promise.noconflict.html)。在 Babel 构建中并没有这样做。
猜你喜欢
  • 2021-11-06
  • 2019-01-16
  • 2014-06-05
  • 2020-02-29
  • 1970-01-01
  • 2021-11-08
  • 2014-11-22
  • 2017-11-24
相关资源
最近更新 更多