错误处理 redux-promise-middleware

IT技术 javascript reactjs redux redux-promise-middleware
2021-05-23 08:08:02

我正在学习 React,以及几乎所有围绕它的必要技术 - 所以我经常被我应该已经知道的事情绊倒。

我在处理异步事件时遇到了错误。我已经在网上搜索过,但没有什么能真正回答我正在寻找的问题。

我目前正在使用 redux 和 redux-promise-middleware 来处理异步操作,如下所示:

export function myFunc() {
  return {
    type: FETCH_FUNC,
    payload: new Promise((resolve, reject) => {
      fetch ('some/url/location/from/which/to/fetch')
        .then( response => {
          if (!response.ok){
            throw new Error(response);
            }
          resolve(response.json());
        }).catch(error => {
          reject(error);
        }),
    })
  };
}

这里有两件事:首先,代码在没有错误的情况下工作得很好。但是,当我故意在代码中创建错误时,正确的方法正在触发,但最终我的控制台中仍然出现以下错误:

Uncaught (in promise) Error: [object Response]

.catch(...) 块不应该处理这个吗?我错过了什么?我应该得到这个吗?如果是这样,为什么?

其次,我读到将 fetch 包装在新的 Promise 中是一种反模式,并且几乎暗示这可能是导致问题的原因。我遇到的所有示例都以这种方式使用它。什么是替代方案?如何在没有包装器的情况下触发解决/拒绝以调度下一个操作?

任何帮助将不胜感激。感谢网络高手。

-------------编辑1----------------

从官方redux-promise-middlewaregithub示例中,他们有以下代码:

export default function request(url, options) {
  return new Promise((resolve, reject) => {
    if (!url) reject(new Error('URL parameter required'));
    if (!options) reject(new Error('Options parameter required'));

    fetch(url, options)
      .then(response => response.json())
      .then(response => {
        if (response.errors) reject(response.errors);
        else resolve(response);
      })
      .catch(reject);
  });
}

中间件的意图似乎是包装fetch在 a 中new Promise并捕获任何rejects。如果有人有一种可行的替代方法来实现这一点redux-promise-middleware,或者可以详细说明为什么它遵循这种模式,将不胜感激。

-------------编辑2----------------

不确定实现这一点的预期方式是什么,或者如何避免Promise中的 Uncaught 错误。Promise.reject(...)除非您包含错误处理函数,否则简单地调用会导致未捕获的错误:Promise.reject(...).then(() =>{...}, error => {...}). 在中间件中包含这个会导致被拒绝的动作永远不会被分派。我已经远离 redux-promise-middleware,直到找到合适的修复和/或实现。

3个回答

我想你得到的是预期的结果,中间件文档中清楚地提到了这一点

中间件分派被拒绝的操作,但不会捕获被拒绝的Promise。因此,您可能会在控制台中收到“未捕获”警告。这是未捕获的被拒绝Promise的预期行为。捕获错误是您的责任,而不是 redux-promise-middleware 的责任。

但是,如果您询问最佳实践,这就是我很久以前最终做的事情,并且与我完美配合:

1- 对于某些Promise,您可以按照文档中的说明进行操作:

dispatch({
    type: 'FOO_ACTION',
    payload: new Promise(() => {
      throw new Error('foo');
    })
  }).catch(error => {
    // catch and handle error or do nothing
  });

2- 要全局捕获所有被拒绝的Promise,请在 redux-promise-middleware 之前添加此中间件,如下所示:

/**
 * a utility to check if a value is a Promise or not
 * @param value
 */
const isPromise = value => value !== null && typeof value === 'object' && typeof value.then === 'function';


export default () => {

  const middleWares = [];

  // global error middleware
  middleWares.push(() => next => action => {

    // If not a promise, continue on
    if (!isPromise(action.payload)) {
      return next(action);
    }

    /**
     * include a property in `meta and evaluate that property to check if this error will be handled locally
     *
     * if (!action.meta.localError) {
     *   // handle error
     * }
     *
     * The error middleware serves to dispatch the initial pending promise to
     * the promise middleware, but adds a `catch`.
     */
    if (!action.meta || !action.meta.localError) {
      // Dispatch initial pending promise, but catch any errors
      return next(action).catch(error => {
        if (config.showErrors) { // here you can decide to show or hide errors
          console.log(`${action.type} unhandled rejection caught at middleware with reason: ${JSON.stringify(error.message)}.`);
        }
        return error;
      });
    }

    return next(action);
  });

  // middleware
  middleWares.push(thunk);
  middleWares.push(promise());  
  middleWares.push(logger());

  return applyMiddleware(...middleWares);
}

我想这正是你要找的 ;)

额外的,我强烈建议Axios公司在获取,原因如下:

  • 如果请求有错误代码,则 axios module会自动拒绝Promise,这是您需要在 fetch 中手动处理的内容
  • 在 axios 中,您可以使用默认的 base-url、header、interceptors 创建实例...
  • 在 axios 中,您可以使用令牌取消任何先前的请求,这对于自动完成和聊天应用程序特别有用
  • 此外,axios 在内部自动切换xhrhttpmodule以根据环境(NodeJs 或浏览器)执行 ajax 请求,我个人在电子、nodejs、浏览器和 react-native 中使用了相同的 redux 操作,并且一切正常

跟进 caisah 的评论,摆脱间接。您可以通过简单地解决或拒绝新的Promise对象来解决或拒绝Promise

export function myFunc() {
  return {
    type: FETCH_FUNC,
    payload: fetch ('some/url/location/from/which/to/fetch')
        .then(response => {
          if (!response.ok){
            throw new Error(response);
          }
          return Promise.resolve(response.json());
        }).catch(error => {
          return Promise.reject(error)
        }),
    })
  };
}

myFunc().payload.then(json => /* do stuff with json*/)

PS 回报可能是多余的。

我已经使用了“Catching Errors Throwns by Rejected Promises”中介绍的“Catching Errors Globally”,如图所示,当调用 applyMiddleware 时,errorMiddleware 应该在 promiseMiddleware 之前。要过滤应用此中间件的操作类型,我更喜欢正则表达式:

这是商店的创建:

import { createStore, combineReducers, applyMiddleware } from 'redux';
import promiseMiddleware from 'redux-promise-middleware';
import errorMiddleware from './errorMiddleware';

import adultosReducer from './adultosReducer';

const rootReducer = combineReducers({
  adultosReducer
});

const composeStoreWithMiddleware = applyMiddleware(errorMiddleware, promiseMiddleware())(
  createStore
);

export default composeStoreWithMiddleware(rootReducer);

这是错误中间件:

import isPromise from 'is-promise';
import _ from 'lodash';

const tiposAction = /^ADULTO/i;

export default function errorMiddleware() {
  return next => action => {
    // If not a promise, continue on
    if (!isPromise(action.payload)) {
      return next(action);
    }

    console.log('errorMiddleware: action.type', action.type);
    if (action.type.match(tiposAction)) {
      // Dispatch initial pending promise, but catch any errors
      return next(action).catch(error => {
        console.log('catching action', error);

        return error;
      });
    }

    return next(action);
  };
}

这样你就可以温和地向用户显示错误,因为被拒绝的操作是在没有 Unhandled Promise的情况下调度的。当然也不需要添加 redux-thunk。