JavaScript使用promise处理多重复请求

 更新时间:2021年05月26日 09:28:07   作者:专业逮虾户aaa  
处理重复请求的文章想必大家也看过了很多,大多数都是分为在response返回之前发现重复请求就return掉的和使用节流/防抖来间接规避用户频繁操作两种版本的。本文主要介绍了JavaScript使用promise处理多重复请求,感兴趣的可以了解一下

一、为什么要写这个文章?

处理重复请求的文章想必大家也看过了很多,大多数都是分为在response返回之前发现重复请求就return掉的和使用节流/防抖来间接规避用户频繁操作两种版本的。最近在使用的过程的中,发现这两个版本在某些场景下还是有些局限性。

二、问题场景

如图,我这个h5的页面,顶部和底部都要显示这个名片组件。这些名片的信息是通过一个接口来获取的,当这个组件在当前页面被初始化时,就会发生两次重复的请求。

这时会面临几个抉择:

1. 不对重复请求做任何处理。

  • 缺点1:造成不必要的资源浪费,增大服务器的压力
  • 缺点2:http请求在浏览器中是有并发数限制的,如果页面首屏的请求较多且没有分层级加载的话,很容易造成请求阻塞,影响用户第一时间看到主要内容

2. 对重复请求直接return掉。这也是部分文章的做法,不过这种做法有种局限性,就是直接认定后面的重复请求均为无效请求。

  • 无效请求场景:用户点击了某个按钮进行查询或保存,在请求结果返回之前,后面点击基本都算是无效请求,这种请求就是应该被阻止的。当然,也可以通过在按钮上添加节流/防抖来规避这个问题
  • 为何不适用于目前场景:这两个名片的组件都是需要数据来渲染的,如果第二次重复的请求被return了,其中一个组件的名片就会没有数据。

3. 把请求从组件中抽离出来放到父级的业务页面中,再以props的方式传进组件。

  • 好处:只需要请求一次,两个组件就可以共享一份数据。
  • 局限性:只适用于单个业务页面用到的情况。事实上这个组件很多个业务页面在用,即使把请求的函数抽成公用的api,也是要在每个业务页面初始化的时候调用一次,然后再以props的方式传进组件。

三、解决方式

核心思想

  • 初始化一个handleList的数组
  • 在请求发送前,根据入参是否相同判断是否为重复请求
    • 非重复请求:把改请求的参数和请求返回的Promise添加至数组中
    • 重复请求:使用find查找直接返回对应的Promise
  • 请求完成后把handleList中之前添加的请求信息移除。

这个方案是什么都可以使用的,无论是使用axios、jq、fetch、小程序request。这里就写实现的原理,使用时直接把对应的代码放到对应的请求时机即可。

代码示例

let handleList = [] // 请求列表
/**
 * 模拟请求
 * @author waldon
 * @date 2020/6/9
 */
const httpRequest = () => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(`请求成功,时间戳为:${new Date().getTime()}`)
    }, 1000)
  })
}
/**
 * 请求的相关处理
 * @author waldon
 * @date 2020/6/9
 * @param {String} url -
 * @param {Object} requestObj - 请求参数
 * @returns {Promise} - 请求的promise
 */
function requestTest(url, requestObj = {}) {
  // 因为入参一般不会涉及到复杂类型,JSON.stringify进行序列化对比其实够用了
  // 有个局限性就是入参的顺序改变了就会影响判断,不过这种特殊的改变一般在重复请求中不会出现
  // 实在是有这种需求的,换成其他递归对比的api,lodash也有类似的api
  const sameHandle = handleList.find(
    (item) => item.url === url && JSON.stringify(item.requestObj) === JSON.stringify(requestObj)
  )
  if (sameHandle) {
    // 遇到相同请求直接返回之前请求的promise
    console.log(`存在重复请求,直接返回`)
    return sameHandle.handle
  }
  const handle = new Promise((resolve, reject) => {
    httpRequest()
      .then((res) => {
        resolve(res)
      })
      .catch((err) => {
        reject(err)
      })
      .finally(() => {
        // 无论请求结果如果,都需要把对应的请求移除掉
        handleList = handleList.filter(
              (item) =>
                item.url !== url && JSON.stringify(item.requestObj) !== JSON.stringify(requestObj)
            )
      })
  })
  handleList.push({ url, requestObj, handle })
  return handle
}

// *******************************我是华丽的分割线 开始使用*******************************
const params = {
  name: 'waldon'
}
requestTest('/ajax/sameUrl', params).then((res) => {
  console.log(`首次请求结果`, res)
  console.log(`handleList:`, handleList)
})
requestTest('/ajax/sameUrl', params).then((res) => {
  console.log(`重复请求结果`, res)
  console.log(`handleList:`, handleList) // 请求列表中始终只有一个请求
  setTimeout(() => {
    console.log(`请求完成后的handleList:`, handleList) // 请求完成handleList对应的请求会被清除
  }, 100)
})
setTimeout(() => {
  // 特意延迟500ms请求,因为我们设置了接口1s才返回,所以应该得到一样的结果
  requestTest('/ajax/sameUrl', params).then((res) => {
    console.log(`重复请求结果`, res)
    console.log(`handleList:`, handleList)
  })
}, 500)

输出结果

存在重复请求,直接返回
存在重复请求,直接返回
首次请求结果 请求成功,时间戳为:1621650375540
handleList: [
  {
    url: '/ajax/sameUrl',
    requestObj: { name: 'waldon' },
    handle: Promise { '请求成功,时间戳为:1621650375540' }
  }
]
重复请求结果 请求成功,时间戳为:1621650375540
handleList: [
  {
    url: '/ajax/sameUrl',
    requestObj: { name: 'waldon' },
    handle: Promise { '请求成功,时间戳为:1621650375540' }
  }
]
重复请求结果 请求成功,时间戳为:1621650375540
handleList: [
  {
    url: '/ajax/sameUrl',
    requestObj: { name: 'waldon' },
    handle: Promise { '请求成功,时间戳为:1621650375540' }
  }
]
请求完成后的handleList: []

代码地址 codepen

https://codepen.io/waldonUB/pen/ZEeeONM

注意的点

  • 不要对response中的数据进行增删操作。因为重复请求返回Promise中的对象引用地址都是同一个,改动了就会造成数据污染。特殊情况时可以浅拷贝响应结果再处理,或者是增加对应的断言。
  • 处理重复的请求时,最好在log中提示一下,同时在组件中注释好原因和使用场景,避免他人误改
  • 做好极端情况下,请求失败的处理,设置有效时间置空和移除请求信息,避免因为闭包堆积过多无用的请求信息造成内存泄漏。

到此这篇关于JavaScript使用promise处理多重复请求的文章就介绍到这了,更多相关js promise多重复请求内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • js+html实现点名系统功能

    js+html实现点名系统功能

    这篇文章主要为大家详细介绍了js+html实现点名系统功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-11-11
  • javascript中expression的用法整理

    javascript中expression的用法整理

    在学习javaScript, 学到regular expression的时候见知识点有些杂乱,于是将其整理了一些在本文与大家分享
    2014-05-05
  • JavaScript中的继承之类继承

    JavaScript中的继承之类继承

    在JS中继承是一个非常复杂的话题,比其他任何面向对象语言中的继承都复杂得多。接下来通过本文给大家介绍JavaScript中的继承之类继承,感兴趣的朋友一起看看吧
    2016-05-05
  • 微信小程序绘制半圆(弧形)进度条

    微信小程序绘制半圆(弧形)进度条

    这篇文章主要为大家详细介绍了微信小程序绘制半圆(弧形)进度条,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-11-11
  • localStorage设置有效期和过期时间的简单方法

    localStorage设置有效期和过期时间的简单方法

    众所周知前端三大缓存,cookie,sessionStorage,localStorage,下面这篇文章主要给大家介绍了关于localStorage设置有效期和过期时间的相关资料,需要的朋友可以参考下
    2022-02-02
  • 改变checkbox默认选中状态及取值的实现代码

    改变checkbox默认选中状态及取值的实现代码

    下面小编就为大家带来一篇改变checkbox默认选中状态及取值的实现代码。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-05-05
  • javascript获取鼠标点击元素对象(示例代码)

    javascript获取鼠标点击元素对象(示例代码)

    本篇文章主要介绍了利用javascript获取鼠标点击元素对象的示例代码。需要的朋友可以过来参考下,希望对大家有所帮助
    2013-12-12
  • JavaScript 函数的定义-调用、注意事项

    JavaScript 函数的定义-调用、注意事项

    这篇文章主要介绍了JavaScript 函数的定义-调用、注意事项,需要的朋友可以参考下
    2017-04-04
  • 原生JS实现悬停下拉菜单

    原生JS实现悬停下拉菜单

    这篇文章主要为大家详细介绍了原生JS实现悬停下拉菜单,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09
  • 基于JavaScript实现熔岩灯效果导航菜单

    基于JavaScript实现熔岩灯效果导航菜单

    这篇文章主要介绍了基于JavaScript实现熔岩灯效果导航菜单,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-01-01

最新评论