慕森卡
我不知道您的特定 api 是如何工作的,但下面是一个工作示例,说明如何将中止逻辑放入操作和减速器中,当进行新的提取时,它将中止任何先前活动的假提取:import * as React from 'react';import ReactDOM from 'react-dom';import { createStore, applyMiddleware, compose,} from 'redux';import { Provider, useDispatch, useSelector,} from 'react-redux';import { createAsyncThunk, createSlice,} from '@reduxjs/toolkit';const initialState = { entities: [],};// constant value to reject with if abortedconst ABORT = 'ABORT';// fake signal constructorfunction Signal() { this.listener = () => undefined; this.abort = function () { this.listener(); };}const fakeFetch = (signal, result, time) => new Promise((resolve, reject) => { const timer = setTimeout(() => resolve(result), time); signal.listener = () => { clearTimeout(timer); reject(ABORT); }; });// will abort previous active request if there is oneconst latest = (fn) => { let previous = false; return (signal, result, time) => { if (previous) { previous.abort(); } previous = signal; return fn(signal, result, time).finally(() => { //reset previous previous = false; }); };};// fake fetch that will abort previous active is there is oneconst latestFakeFetch = latest(fakeFetch);const fetchUserById = createAsyncThunk( 'users/fetchByIdStatus', async ({ id, time }) => { const response = await latestFakeFetch( new Signal(), id, time ); return response; });const usersSlice = createSlice({ name: 'users', initialState: { entities: [], loading: 'idle' }, reducers: {}, extraReducers: { [fetchUserById.fulfilled]: (state, action) => { state.entities.push(action.payload); }, [fetchUserById.rejected]: (state, action) => { if (action?.error?.message === ABORT) { //do nothing } }, },});const reducer = usersSlice.reducer;//creating store with redux dev toolsconst composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;const store = createStore( reducer, initialState, composeEnhancers( applyMiddleware( ({ dispatch, getState }) => (next) => (action) => typeof action === 'function' ? action(dispatch, getState) : next(action) ) ));const App = () => { const dispatch = useDispatch(); React.useEffect(() => { //this will be aborted as soon as the next request is made dispatch( fetchUserById({ id: 'will abort', time: 200 }) ); dispatch(fetchUserById({ id: 'ok', time: 100 })); }, [dispatch]); return 'hello';};ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root'));如果您只需要解决一个承诺(如果它是最新请求的承诺),而无需中止或取消正在进行的承诺(如果不是最新的则忽略解决),那么您可以执行以下操作:const REPLACED_BY_NEWER = 'REPLACED_BY_NEWER';const resolveLatest = (fn) => { const shared = {}; return (...args) => { //set shared.current to a unique object reference const current = {}; shared.current = current; fn(...args).then((resolve) => { //see if object reference has changed // if so it was replaced by a newer one if (shared.current !== current) { return Promise.reject(REPLACED_BY_NEWER); } return resolve; }); };};这个答案演示了如何使用它
天涯尽头无女友
我能够将其放在一起,但它非常复杂。以下函数创建执行实际工作的“内部”异步 thunk,以及委托给内部异步 thunk 并中止之前的调度(如果有)的“外部”异步 thunk。内部 thunk 的有效负载创建者也被包装为:1)等待有效负载创建者的先前调用完成,2)如果操作在等待期间中止,则跳过调用真正的有效负载创建者(以及 API 调用)。import { createAsyncThunk, AsyncThunk, AsyncThunkPayloadCreator, unwrapResult } from '@reduxjs/toolkit';export function createNonConcurrentAsyncThunk<Returned, ThunkArg>( typePrefix: string, payloadCreator: AsyncThunkPayloadCreator<Returned, ThunkArg>, options?: Parameters<typeof createAsyncThunk>[2]): AsyncThunk<Returned, ThunkArg, unknown> { let pending: { payloadPromise?: Promise<unknown>; actionAbort?: () => void; } = {}; const wrappedPayloadCreator: AsyncThunkPayloadCreator<Returned, ThunkArg> = (arg, thunkAPI) => { const run = () => { if (thunkAPI.signal.aborted) { return thunkAPI.rejectWithValue({name: 'AbortError', message: 'Aborted'}); } const promise = Promise.resolve(payloadCreator(arg, thunkAPI)).finally(() => { if (pending.payloadPromise === promise) { pending.payloadPromise = null; } }); return pending.payloadPromise = promise; } if (pending.payloadPromise) { return pending.payloadPromise = pending.payloadPromise.then(run, run); // don't use finally(), replace result } else { return run(); } }; const internalThunk = createAsyncThunk(typePrefix + '-protected', wrappedPayloadCreator); return createAsyncThunk<Returned, ThunkArg>( typePrefix, async (arg, thunkAPI) => { if (pending.actionAbort) { pending.actionAbort(); } const internalPromise = thunkAPI.dispatch(internalThunk(arg)); const abort = internalPromise.abort; pending.actionAbort = abort; return internalPromise .then(unwrapResult) .finally(() => { if (pending.actionAbort === abort) { pending.actionAbort = null; } }); }, options );}