封装 axios 拦截器实现用户无感刷新 access_token

### Optimizing the Processing Logic for the `requests` Array To address the issue of refreshing `access_token` when multiple requests are initiated simultaneously, the following key logic must be implemented in the code: --- ### **1. Optimizing the `requests` Array Processing Logic** When multiple requests are initiated concurrently, ensure that all pending requests are correctly executed after `access_token` refresh. ```javascript let isRefreshing = false; let requests = []; // Store pending requests ``` **Key Logic:** - After `refreshToken` succeeds, iterate through the `requests` array and execute callback functions sequentially. - Clear the array after refresh to avoid reprocessing. ```javascript instance.interceptors.response.use( response => response, error => { if (!error.response) return Promise.reject(error); if (error.response.status === 401 && !error.config.url.includes('/auth/refresh')) { const { config } = error; if (!isRefreshing) { isRefreshing = true; return refreshToken().then(res => { const { access_token } = res.data; setToken(access_token); config.headers.Authorization = `Bearer ${access_token}`; // Execute all pending requests requests.forEach(cb => cb(access_token)); requests = []; return instance(config); }).catch(err => Promise.reject(err)).finally(() => { isRefreshing = false; }); } else { // Save unresolved Promise for subsequent processing return new Promise(resolve => { requests.push(token => { config.headers.Authorization = `Bearer ${token}`; resolve(instance(config)); }); }); } } return Promise.reject(error); } ); ``` --- ### **2. Ensuring All Requests Are Correctly Executed After Refresh** After `refreshToken` succeeds, ensure all pending request callbacks are executed: ```javascript requests.forEach(cb => cb(access_token)); requests = []; ``` **Explanation:** - Each element in `requests` is a callback function (e.g., `cb(access_token)`). - After refresh, callbacks are executed sequentially. - Clearing the array prevents duplicate processing. --- ### **3. Handling Unresolved Promises on Token Refresh Failure** When `refreshToken` fails, save the unresolved Promise to the `requests` array for subsequent handling: ```javascript return new Promise(resolve => { requests.push(token => { config.headers.Authorization = `Bearer ${token}`; resolve(instance(config)); }); }); ``` **Key Points:** - `resolve` is saved to `requests` for later execution. - When refresh succeeds, all callbacks are invoked to reissue pending requests. --- ### **4. Preventing Multiple Token Refreshes** Use the `isRefreshing` flag to control refresh state and avoid simultaneous requests: ```javascript if (!isRefreshing) { isRefreshing = true; return refreshToken().then(...); } ``` **Explanation:** - If refresh is in progress, new requests are returned with unresolved Promises to avoid duplicate processing. --- ### **Final Optimized Code** ```javascript // request.js import axios from 'axios'; import { getToken, setToken, getRefreshToken } from './auth'; const instance = axios.create({ baseURL: process.env.GATSBY_API_URL, timeout: 30000, headers: { 'Content-Type': 'application/json' } }); let isRefreshing = false; let requests = []; instance.interceptors.response.use( response => response, error => { if (!error.response) return Promise.reject(error); if (error.response.status === 401 && !error.config.url.includes('/auth/refresh')) { const { config } = error; if (!isRefreshing) { isRefreshing = true; return refreshToken().then(res => { const { access_token } = res.data; setToken(access_token); config.headers.Authorization = `Bearer ${access_token}`; requests.forEach(cb => cb(access_token)); requests = []; return instance(config); }).catch(err => Promise.reject(err)).finally(() => { isRefreshing = false; }); } else { return new Promise(resolve => { requests.push(token => { config.headers.Authorization = `Bearer ${token}`; resolve(instance(config)); }); }); } } return Promise.reject(error); } ); const setHeaderToken = (isNeedToken) => { const accessToken = isNeedToken ? getToken() : null; if (isNeedToken) { instance.defaults.headers.common.Authorization = `Bearer ${accessToken}`; } }; export const get = (url, params = {}, isNeedToken = false) => { setHeaderToken(isNeedToken); return instance({ method: 'get', url, params }); }; export const post = (url, data, isNeedToken = false) => { setHeaderToken(isNeedToken); return instance({ method: 'post', url, data }); }; ``` --- ### **Summary** - **Key Logic:** Use the `requests` array to store pending requests, ensuring all are executed after `access_token` refresh. - **Prevent Duplicate Refreshes:** The `isRefreshing` flag controls refresh state, avoiding simultaneous requests. - **Handle Failures:** Save unresolved Promises to the array for subsequent processing. This approach effectively resolves the issue of refreshing `access_token` when multiple requests are initiated, ensuring all requests are correctly handled after token updates.