对一个密集请求问题的优化思考

184 阅读1分钟

只想看题的可以直接往下

前情提要: 有一个列表页,会密集请求列表 ({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()
 }