RxJS去除重复请求:如何避免获取帖子时重复请求同一用户?
Avoiding Duplicate User Requests in Redux-Observable Epic
Great question—redundant API calls are a common pitfall, and your existing code already has the first piece of the puzzle (checking the cache). To fully prevent duplicate requests (including in-flight ones), we need to track ongoing requests so we don’t fire a new call while one’s already in progress for the same user.
Here’s how to modify your Epic to handle both cached users and pending requests:
// Track pending user fetch requests (persists across Epic calls since Epics are singletons) const pendingRequests = new Map(); const fetchUserEpic = (action$, store) => action$ .ofType(UsersActions.FETCH_USER) .mergeMap(action => { const userId = action.payload; const users = store.getState().usersStore.get('users').toJS(); // 1. Check if user is already cached in the store if (users[userId]) { // Return action indicating we're using cached data (include userId for context) return of(UsersActionCreators.loadCachedUser(userId)); } // 2. Check if there's already an in-flight request for this userId if (pendingRequests.has(userId)) { // Reuse the existing pending observable instead of making a new API call return pendingRequests.get(userId); } // 3. No cache or pending request—initiate the API call const userFetch$ = ajax.getJSON(`/api/users/${userId}`) .map(userData => UsersActionCreators.fetchUserSuccess(userData)) .catch(error => of(UsersActionCreators.fetchUserFailure(userId, error))) .finally(() => { // Clean up: remove the request from pending once it completes (success/error) pendingRequests.delete(userId); }); // Add the ongoing request to our tracking map pendingRequests.set(userId, userFetch$); return userFetch$; });
Key Improvements Explained:
- Pending Request Tracking: The
pendingRequestsMap keeps tabs on all in-progress API calls by userId. This ensures we don’t send duplicate requests while one’s already underway. - Cache Check First: We still prioritize the store cache to avoid unnecessary network calls entirely if the user data is already available.
- Reuse In-Flight Observables: If a request is already pending for a userId, we return that existing observable instead of creating a new one. This means any subscribers to the action will receive the result of the ongoing request once it finishes.
- Cleanup: The
finallyblock ensures we remove completed requests from the map, so future actions can trigger a new request if needed (e.g., retrying after a failed call).
Bonus Notes:
- Make sure your
loadCachedUseraction includes the userId so your reducer knows which user to mark as cached (if needed). - If you’re using TypeScript, you can type the
pendingRequestsMap to enforce userId types and observable types for better type safety.
内容的提问来源于stack exchange,提问作者Eyal
相关产品推荐
相关产品推荐

