解决方案——多次发送同一请求,仅响应最后一次
问题来源
由于查询条件改变频率高,或者后端数据处理逻辑复杂,导致接口返回的顺序跟实际请求的顺序不一致。
如:同一接口,先用复杂度高的查询条件发A请求,然后在A还没返回时发简单的B请求,且B返回数据后A才返回,此时若是用 *.then(res => …) 来处理的话,会出现最终使用的是A的返回数据的情况。
解决思路
由于涉及的场景不算复杂,故不打算使用类,个人选择了工厂函数的模式,考虑用两个变量分别保存当前已发送的请求数量,及当前请求的索引,分别记为A,B
A初始为0,每次请求时加一,并将A赋值给该请求的索引B,请求返回时对比A和B,则:
- 若 A > B,说明不是我们期望的请求,在返回的数据中作标记,并在请求返回的回调函数中进一步处理
- 若 A = B,说明是我们应该处理的请求,将A置为0并返回原数据即可
正确的实现不应该出现 A < B 的情况
代码
/** 例如:
* const fetchOptions = limitRequest()
* fetchOptions({ url: 'http://www.baidu.com' }).then(res => { console.log(res) })
**/
const limitRequest = function() {
const num = new Map() // 请求数量
num.set('_default', 0) // 默认情况
return function(reqObj, requestKey = '_default') { // requestKey为请求的唯一标识符,当需要标识不同请求进行拦截时必传
if (!num.has(requestKey)) { num.set(requestKey, 0) } // 若key不存在则设置
const keyNum = num.get(requestKey) + 1 // 每次调用请求数量加一
num.set(requestKey, keyNum) // 更新map请求数量
const idx = keyNum // 请求索引
return request(reqObj).then(res => {
if (num.get(requestKey) === idx) {
num.set(requestKey, 0)
} else { res._beIntercepted = true }
return res
}).catch(err => Promise.reject(err))
}
}
注:代码中除了实现上述逻辑之外,还用hash表标记了期望处理的不同请求,这样可以避免为每个期望处理的请求都调用一次工厂函数