Redux中间件dispatch返回无结果Promise的原因及解决方法
接手一个使用 vanilla Redux 及中间件的 React 项目,Redux 配置如下:
composeEnhancers(applyMiddleware(...middleware.map(f => f(services))))
其中middleware是包含多个函数的中间件数组,services是注入中间件的外部服务对象(如 api 等)。
项目中的中间件示例:
// ... const throwErrorFlow = ({ api }) => ({ dispatch, getState }) => next => async (action) => { next(action) if (action.type === actions.THROW_ERROR) { try { dispatch(actions.setLoadingSlot({ state: false, context: action.payload.context })) const context = getState().ui.context const payload = { location: action.payload.location, error: action.payload.error?.stack, context } console.log(payload); await api.context.throwError(payload) dispatch(actions.setErrorModalVisibility({ payload, visibility: true })) } catch (error) { console.log(error); } } } const middleware = [ middlwareFunction1, middlwareFunction2, // ... throwErrorFlow ] export default middleware
我创建了一个返回测试字符串的自定义 action 及对应中间件:
自定义 action 代码:
export const customAction = payload => ({ type: CUSTOM_ACTION, payload: payload, })
自定义中间件代码:
const customAsyncActionFlow = () => storeAPI => next => action => { if (action.type === actions.CUSTOM_ACTION) { console.log(action); return 'TEST!' } return next(action); } const middleware = [ middlwareFunction1, middlwareFunction2, // ... throwErrorFlow, customAsyncActionFlow ] export default middleware
在 UI 中调用时:
console.log(dispatch(customAction('Hello World!')));
action 能正常打印,但 dispatch 返回的是Promise {<fulfilled>: undefined}而非预期的'TEST!'。仅保留自定义中间件时一切正常。
疑问点:
- 无结果 Promise 的来源是什么?是否与其他中间件无返回值有关?
- 如何在保留所有中间件的情况下让 dispatch 返回预期结果?
- 项目中每个需与 api 交互的 action 都对应一个中间件(共 20 个),为何自定义返回值会失效?
- 能否让自定义 action 仅触发对应的中间件?
1. 无结果 Promise 的来源
问题出在项目中存在async 类型的中间件(比如throwErrorFlow):
- async 函数默认会返回一个 Promise,无论是否显式 return。
- 这些中间件调用
next(action)后,没有返回next(action)的执行结果,函数执行完毕后默认返回undefined,最终导致整个 dispatch 链的返回值变成状态为 fulfilled、值为 undefined 的 Promise。
2. 自定义返回值失效的原因
Redux 中间件是按数组顺序链式执行的:
- 自定义中间件排在所有中间件的最后,而前面的 async 中间件已经把返回值替换成了自己的 Promise,后续中间件的返回值无法覆盖这个 Promise 的最终结果。
- 只有中间件链中没有 async 中间件时,自定义返回值才能直接被 dispatch 返回。
3. 保留所有中间件并返回预期结果的方案
有两种可行的解决方式:
方案一:修复所有 async 中间件的返回逻辑
确保每个中间件都返回next(action)的结果,保证返回值在中间件链中正确传递:
// 修改后的 throwErrorFlow 示例 const throwErrorFlow = ({ api }) => ({ dispatch, getState }) => next => async (action) => { // 先获取 next(action) 的执行结果 const result = next(action); if (action.type === actions.THROW_ERROR) { try { // ... 原有业务逻辑不变 dispatch(actions.setLoadingSlot({ state: false, context: action.payload.context })) const context = getState().ui.context const payload = { location: action.payload.location, error: action.payload.error?.stack, context } console.log(payload); await api.context.throwError(payload) dispatch(actions.setErrorModalVisibility({ payload, visibility: true })) } catch (error) { console.log(error); } } // 返回 next 的结果,保证中间件链的返回值传递 return result; }
这样,处理非目标 action 时,中间件会将后续中间件的返回值继续传递,最终自定义中间件返回的'TEST!'会被正确传递到 dispatch 的返回值中。
方案二:调整自定义中间件的顺序+处理返回值
把自定义中间件移到所有 async 中间件的前面:
const middleware = [ customAsyncActionFlow, // 移到最前面 middlwareFunction1, middlwareFunction2, // ... throwErrorFlow ]
此时 dispatch 返回的会是Promise {<fulfilled>: 'TEST!'}(后续的 async 中间件会把非 Promise 值包装成 Promise),如果需要原始的'TEST!',可以在调用时用await:
const result = await dispatch(customAction('Hello World!')); console.log(result); // 输出'TEST!'
4. 让自定义 action 仅触发对应中间件的方法
Redux 中间件默认会接收所有 action,要实现仅触发对应中间件,可以在自定义中间件中处理完后不调用next(action),阻断 action 向后传递:
const customAsyncActionFlow = () => storeAPI => next => action => { if (action.type === actions.CUSTOM_ACTION) { console.log(action); return 'TEST!' } // 非自定义 action 才继续传递给下一个中间件 return next(action); }
注意:这种方式会让自定义 action 无法到达 reducer,如果自定义 action 需要触发 reducer 更新状态,还是要调用next(action),同时结合方案一修复其他中间件的返回逻辑。
内容的提问来源于stack exchange,提问作者A Tayea

