2016-11-10 81 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