只想看题的可以直接往下
前情提要: 有一个列表页,会密集请求列表 ({pageNum:1,pageSize:20},{pageNum:2,pageSize:20},{pageNum:3,pageSize:20}...),但是服务端做了限制,同时发起的请求。会响应:请求过于频繁,请稍后再试,导致第二页第三页等请求全部失效了。
当时项目紧急,就让服务端放开了限制,同时把pageSize调大临时处理的。现在有空闲了,所以打算总结一下,把业务逻辑大致抽象了一下:
/*
修改getList让控制台顺序输出1 - 10
*/
let lastTimestamp
function fetchList (params) {
return new Promise((resolve, reject) => {
if (lastTimestamp && (Date.now() - lastTimestamp) <= 4000) {
reject(new Error("请求过于频繁"))
}
lastTimestamp = Date.now()
window.setTimeout(() => {
resolve(params.index)
}, 1000)
})
}
let index = 1
function getList () {
const params = { index }
fetchList(params).then(res => {
console.log(res)
})
index++
}
// test
for (let i = 0; i < 10; i++) {
getList()
}
答案部分:自己琢磨的,有不妥的地方欢迎指正,只不过我不怎么上线...
let lastTimestamp
function fetchList (params) {
return new Promise((resolve, reject) => {
if (lastTimestamp && (Date.now() - lastTimestamp) <= 4000) {
reject(new Error("请求过于频繁"))
}
lastTimestamp = Date.now()
window.setTimeout(() => {
resolve(params.index)
}, 1000)
})
}
let index = 1
class TaskQueue {
#fetchQueue;
#waitQueue;
#isFetching;
milliseconds;
constructor (milliseconds = 4000) {
this.#fetchQueue = [] // 执行队列
this.#waitQueue = [] // 等待队列
this.#isFetching = false
this.milliseconds = milliseconds
}
add (task) {
this.#waitQueue.push(task)
this.#walkQueue()
}
#getQueue () {
if (this.#fetchQueue.length === 0) {
this.#fetchQueue = this.#waitQueue
this.#waitQueue = []
}
return this.#fetchQueue
}
async #walkQueue () {
if (this.#isFetching) {
return
}
this.#isFetching = true
const queue = this.#getQueue()
if (queue.length === 0) {
return
}
while (queue.length) {
const task = queue.shift()
await task.fn.(task.params)
await new Promise((resolve, reject) => {
window.setTimeout(() => { resolve() }, this.milliseconds)
})
}
// 自调用,检查waitQueue是否有新的task
this.#isFetching = false
this.#walkQueue()
}
}
const taskQueue = new TaskQueue()
function getList () {
const params = { index }
taskQueue.add({
fn: function () {
fetchList(params).then(res => {
console.log(res)
})
}.bind(this),
params
})
index++
}
// test
for (let i = 0; i < 10; i++) {
getList()
}
下午想到第二种方法,补充一下:
let lastTimestamp
function fetchList (params) {
return new Promise((resolve, reject) => {
if (lastTimestamp && (Date.now() - lastTimestamp) <= 4000) {
reject(new Error("请求过于频繁"))
}
lastTimestamp = Date.now()
window.setTimeout(() => {
resolve(params.index)
}, 1000)
})
}
function asyncify (fn, milliseconds = 4000) {
let fetchQueue = []
let waitQueue = []
let isDoingTask = false
const _this = this
return async function (...args) {
waitQueue.push({
fn: fn,
params: args
})
if (isDoingTask) {
return
}
isDoingTask = true
;await (async function resolveQueue () {
while (fetchQueue.length) {
const task = fetchQueue.shift()
await task.fn.apply(_this, task.params)
await new Promise((resolve, reject) => {
window.setTimeout(resolve, milliseconds)
})
}
if (waitQueue.length) {
fetchQueue = waitQueue
waitQueue = []
resolveQueue()
}
})()
isDoingTask = false
}
}
let index = 1
const getList = asyncify(function () {
const params = { index }
fetchList(params).then(res => {
console.log(res)
})
index++
}, 4000)
// test
for (let i = 0; i < 10; i++) {
getList()
}