createAsyncThunk:中止先前的请求

我用来createAsyncThunk向某些 API 发出异步请求。在任何给定时刻只能有一个请求处于活动状态。AbortSignal据我所知,如果我从上次调用中返回了 Promise,则可以使用提供的方法中止该请求。问题是,thunk 本身能否以某种方式“自主”中止先前的请求?我正在考虑两种选择:

  • 将最后一个 AbortSignal 保持在状态中。似乎是错误的,因为状态应该是可序列化的。

  • 将最后一个 Promise 及其 AbortSignal 保留在全局变量中。似乎也是错误的,因为,你知道,全局变量。

有任何想法吗?谢谢。


慕勒3428872
浏览 119回答 2
2回答

慕森卡

我不知道您的特定 api 是如何工作的,但下面是一个工作示例,说明如何将中止逻辑放入操作和减速器中,当进行新的提取时,它将中止任何先前活动的假提取:import * as React from 'react';import ReactDOM from 'react-dom';import {&nbsp; createStore,&nbsp; applyMiddleware,&nbsp; compose,} from 'redux';import {&nbsp; Provider,&nbsp; useDispatch,&nbsp; useSelector,} from 'react-redux';import {&nbsp; createAsyncThunk,&nbsp; createSlice,} from '@reduxjs/toolkit';const initialState = {&nbsp; entities: [],};// constant value to reject with if abortedconst ABORT = 'ABORT';// fake signal constructorfunction Signal() {&nbsp; this.listener = () => undefined;&nbsp; this.abort = function () {&nbsp; &nbsp; this.listener();&nbsp; };}const fakeFetch = (signal, result, time) =>&nbsp; new Promise((resolve, reject) => {&nbsp; &nbsp; const timer = setTimeout(() => resolve(result), time);&nbsp; &nbsp; signal.listener = () => {&nbsp; &nbsp; &nbsp; clearTimeout(timer);&nbsp; &nbsp; &nbsp; reject(ABORT);&nbsp; &nbsp; };&nbsp; });// will abort previous active request if there is oneconst latest = (fn) => {&nbsp; let previous = false;&nbsp; return (signal, result, time) => {&nbsp; &nbsp; if (previous) {&nbsp; &nbsp; &nbsp; previous.abort();&nbsp; &nbsp; }&nbsp; &nbsp; previous = signal;&nbsp; &nbsp; return fn(signal, result, time).finally(() => {&nbsp; &nbsp; &nbsp; //reset previous&nbsp; &nbsp; &nbsp; previous = false;&nbsp; &nbsp; });&nbsp; };};// fake fetch that will abort previous active is there is oneconst latestFakeFetch = latest(fakeFetch);const fetchUserById = createAsyncThunk(&nbsp; 'users/fetchByIdStatus',&nbsp; async ({ id, time }) => {&nbsp; &nbsp; const response = await latestFakeFetch(&nbsp; &nbsp; &nbsp; new Signal(),&nbsp; &nbsp; &nbsp; id,&nbsp; &nbsp; &nbsp; time&nbsp; &nbsp; );&nbsp; &nbsp; return response;&nbsp; });const usersSlice = createSlice({&nbsp; name: 'users',&nbsp; initialState: { entities: [], loading: 'idle' },&nbsp; reducers: {},&nbsp; extraReducers: {&nbsp; &nbsp; [fetchUserById.fulfilled]: (state, action) => {&nbsp; &nbsp; &nbsp; state.entities.push(action.payload);&nbsp; &nbsp; },&nbsp; &nbsp; [fetchUserById.rejected]: (state, action) => {&nbsp; &nbsp; &nbsp; if (action?.error?.message === ABORT) {&nbsp; &nbsp; &nbsp; &nbsp; //do nothing&nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; },&nbsp; },});const reducer = usersSlice.reducer;//creating store with redux dev toolsconst composeEnhancers =&nbsp; window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;const store = createStore(&nbsp; reducer,&nbsp; initialState,&nbsp; composeEnhancers(&nbsp; &nbsp; applyMiddleware(&nbsp; &nbsp; &nbsp; ({ dispatch, getState }) => (next) => (action) =>&nbsp; &nbsp; &nbsp; &nbsp; typeof action === 'function'&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ? action(dispatch, getState)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; : next(action)&nbsp; &nbsp; )&nbsp; ));const App = () => {&nbsp; const dispatch = useDispatch();&nbsp; React.useEffect(() => {&nbsp; &nbsp; //this will be aborted as soon as the next request is made&nbsp; &nbsp; dispatch(&nbsp; &nbsp; &nbsp; fetchUserById({ id: 'will abort', time: 200 })&nbsp; &nbsp; );&nbsp; &nbsp; dispatch(fetchUserById({ id: 'ok', time: 100 }));&nbsp; }, [dispatch]);&nbsp; return 'hello';};ReactDOM.render(&nbsp; <Provider store={store}>&nbsp; &nbsp; <App />&nbsp; </Provider>,&nbsp; document.getElementById('root'));如果您只需要解决一个承诺(如果它是最新请求的承诺),而无需中止或取消正在进行的承诺(如果不是最新的则忽略解决),那么您可以执行以下操作:const REPLACED_BY_NEWER = 'REPLACED_BY_NEWER';const resolveLatest = (fn) => {&nbsp; const shared = {};&nbsp; return (...args) => {&nbsp; &nbsp; //set shared.current to a unique object reference&nbsp; &nbsp; const current = {};&nbsp; &nbsp; shared.current = current;&nbsp; &nbsp; fn(...args).then((resolve) => {&nbsp; &nbsp; &nbsp; //see if object reference has changed&nbsp; &nbsp; &nbsp; //&nbsp; if so it was replaced by a newer one&nbsp; &nbsp; &nbsp; if (shared.current !== current) {&nbsp; &nbsp; &nbsp; &nbsp; return Promise.reject(REPLACED_BY_NEWER);&nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; return resolve;&nbsp; &nbsp; });&nbsp; };};这个答案演示了如何使用它

天涯尽头无女友

我能够将其放在一起,但它非常复杂。以下函数创建执行实际工作的“内部”异步 thunk,以及委托给内部异步 thunk 并中止之前的调度(如果有)的“外部”异步 thunk。内部 thunk 的有效负载创建者也被包装为:1)等待有效负载创建者的先前调用完成,2)如果操作在等待期间中止,则跳过调用真正的有效负载创建者(以及 API 调用)。import { createAsyncThunk, AsyncThunk, AsyncThunkPayloadCreator, unwrapResult } from '@reduxjs/toolkit';export function createNonConcurrentAsyncThunk<Returned, ThunkArg>(&nbsp; typePrefix: string,&nbsp; payloadCreator: AsyncThunkPayloadCreator<Returned, ThunkArg>,&nbsp; options?: Parameters<typeof createAsyncThunk>[2]): AsyncThunk<Returned, ThunkArg, unknown> {&nbsp; let pending: {&nbsp; &nbsp; payloadPromise?: Promise<unknown>;&nbsp; &nbsp; actionAbort?: () => void;&nbsp; } = {};&nbsp; const wrappedPayloadCreator: AsyncThunkPayloadCreator<Returned, ThunkArg> = (arg, thunkAPI) => {&nbsp; &nbsp; const run = () => {&nbsp; &nbsp; &nbsp; if (thunkAPI.signal.aborted) {&nbsp; &nbsp; &nbsp; &nbsp; return thunkAPI.rejectWithValue({name: 'AbortError', message: 'Aborted'});&nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; const promise = Promise.resolve(payloadCreator(arg, thunkAPI)).finally(() => {&nbsp; &nbsp; &nbsp; &nbsp; if (pending.payloadPromise === promise) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pending.payloadPromise = null;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; });&nbsp; &nbsp; &nbsp; return pending.payloadPromise = promise;&nbsp; &nbsp; }&nbsp; &nbsp; if (pending.payloadPromise) {&nbsp; &nbsp; &nbsp; return pending.payloadPromise = pending.payloadPromise.then(run, run); // don't use finally(), replace result&nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; return run();&nbsp; &nbsp; }&nbsp; };&nbsp; const internalThunk = createAsyncThunk(typePrefix + '-protected', wrappedPayloadCreator);&nbsp; return createAsyncThunk<Returned, ThunkArg>(&nbsp; &nbsp; typePrefix,&nbsp; &nbsp; async (arg, thunkAPI) => {&nbsp; &nbsp; &nbsp; if (pending.actionAbort) {&nbsp; &nbsp; &nbsp; &nbsp; pending.actionAbort();&nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; const internalPromise = thunkAPI.dispatch(internalThunk(arg));&nbsp; &nbsp; &nbsp; const abort = internalPromise.abort;&nbsp; &nbsp; &nbsp; pending.actionAbort = abort;&nbsp; &nbsp; &nbsp; return internalPromise&nbsp; &nbsp; &nbsp; &nbsp; .then(unwrapResult)&nbsp; &nbsp; &nbsp; &nbsp; .finally(() => {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (pending.actionAbort === abort) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pending.actionAbort = null;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; });&nbsp; &nbsp; },&nbsp; &nbsp; options&nbsp; );}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

JavaScript