2016-11-10 50 views
0

我有以下的中间件,我用它来打电话类似的异步调用:Promise.catch在终极版中间件被调用的无关减速

import { callApi } from '../utils/Api'; 

import generateUUID from '../utils/UUID'; 

import { assign } from 'lodash'; 

export const CALL_API = Symbol('Call API'); 

export default store => next => action => { 
    const callAsync = action[CALL_API]; 

    if(typeof callAsync === 'undefined') { 
    return next(action); 
    } 

    const { endpoint, types, data, authentication, method, authenticated } = callAsync; 

    if (!types.REQUEST || !types.SUCCESS || !types.FAILURE) { 
    throw new Error('types must be an object with REQUEST, SUCCESS and FAILURE'); 
    } 

    function actionWith(data) { 
    const finalAction = assign({}, action, data); 
    delete finalAction[CALL_API]; 

    return finalAction; 
    } 

    next(actionWith({ type: types.REQUEST })); 

    return callApi(endpoint, method, data, authenticated).then(response => { 
    return next(actionWith({ 
     type: types.SUCCESS, 
     payload: { 
     response 
     } 
    })) 
    }).catch(error => { 
    return next(actionWith({ 
     type: types.FAILURE, 
     error: true, 
     payload: { 
     error: error, 
     id: generateUUID() 
     } 
    })) 
    }); 
}; 

我然后在组件的componentWillMount做以下电话:

componentWillMount() { 
    this.props.fetchResults(); 
    this.props.fetchTeams(); 
    } 

fetchTeams例如将派遣一个由中间件处理的动作,看起来像这样:

export function fetchTeams() { 
    return (dispatch, getState) => { 
    return dispatch({ 
     type: 'CALL_API', 
     [CALL_API]: { 
     types: TEAMS, 
     endpoint: '/admin/teams', 
     method: 'GET', 
     authenticated: true 
     } 
    }); 
    }; 
} 

调度这两个成功操作并从还原器返回新状态。这两个减速看起来相同,下面是Teams减速机:

export const initialState = Map({ 
    isFetching: false, 
    teams: List() 
}); 

export default createReducer(initialState, { 
    [ActionTypes.TEAMS.REQUEST]: (state, action) => { 
    return state.merge({isFetching: true}); 
    }, 

    [ActionTypes.TEAMS.SUCCESS]: (state, action) => { 
    return state.merge({ 
     isFetching: false, 
     teams: action.payload.response 
    }); 
    }, 

    [ActionTypes.TEAMS.FAILURE]: (state, action) => { 
    return state.merge({isFetching: false}); 
    } 
}); 

组件然后将呈现调度另一个动作的另一个组件:

render() { 
    <div> 
    <Autocomplete items={teams}/> 
    </div> 
} 

自动完成,然后分派在其componentWillMount一个动作:

class Autocomplete extends Component{ 
    componentWillMount() { 
    this.props.dispatch(actions.init({ props: this.exportProps() })); 
    } 

在SUCCESS减速器调用后调用的自动完成缩减器中发生错误从父组件的componentWillUpdate但由于某种原因,从第一代码片段中间件的catch处理原来的电话和fetchResults被调用:

return callApi(endpoint, method, data, authenticated).then(response => { 
    return next(actionWith({ 
     type: types.SUCCESS, 
     payload: { 
     response 
     } 
    })) 
    }).catch(error => { 
    return next(actionWith({ 
     type: types.FAILURE, 
     error: true, 
     payload: { 
     error: error, 
     id: generateUUID() 
     } 
    })) 
    }); 
}; 

我不明白为什么catch处理程序被调用为我原以为这个承诺已经解决了。

回答

1

我不完全确定,通过阅读代码很难调试。显而易见的答案是因为这一切都发生在与next(actionWith({ type: types.SUCCESS, payload: { response } }))的调用相同的堆栈跟踪中。

因此,在这种情况下:

  1. 中间件:调度成功里面Promise.then
  2. 终极版更新道具
  3. 阵营:呈现新道具
  4. 阵营:componentWillMount
  5. 阵营:调度新动作

如果在任何时候发生错误,它将会冒泡到Promise.then,然后执行Promise.catch回调。

尝试调用setTimeout内部的自动完成提取以使当前堆栈跟踪结束并在下一个“事件循环”中运行提取。

setTimeout(
() => this.props.dispatch(actions.init({ props: this.exportProps() })) 
); 

如果一切正常,那么它的的事实,当发生错误,并从中间件的成功调度一路呈现autocomplete是后函数调用函数调用事件循环尚未完成处理。

注意:您应该考虑使用Redux的环,或者终极版 - 传奇的异步任务,如果你想使用自定义的中间件,也许你可以从如何使你的API请求的异步库一些灵感,以保持从最初的派遣。

+0

你确实是对的。在下一个勾号上分配操作时,我会得到正确的堆栈跟踪。 我认为循环有一些它正在使用的排队。我将深入研究这一点。感谢您的帮助。 – dagda1