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多重复请求内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • JavaScript如何实现防止重复的网络请求的示例

    前言 在开发中,经常会遇到接口重复请求导致的各种问题. 对于重复的网络请求,会导致页面更新多次,发生页面抖动的现象,影响用户体验. 例如当前页面请求还未响应完成,就切换到其他路由,那么这些请求直到响应返回才会中止. 无论从用户体验或者从业务严谨方面来说,取消无用的请求确实是需要避免的. 实现思路 **  1.在发送请求前先拦截当前请求地址 (url + 方法 + 参数): **  2.开启一个请求队列用于保存 当前地址: **  3.每次请求查看请求队列里面有没有当前url地址: **  4.如

  • JS Ajax请求如何防止重复提交

    好长时间没写js代码了刚好遇到这样的问题.我们系统多数表单没有做防止重复提交的. 由于不想在后端这边处理,因为假如由后端处理的话,就需要在页面加载的时候给出一次性的token值,加大了开发的工作量不说,还容易忘记做这个,同时,ajax也不好处理,需要提交失败的话同时返回新的token值. 所以我想在,js这边动手.其实以前和前端提过,久久不见动静,就只好弄块砖丢出去了.思路是,覆盖掉$.ajax,在这里面处理掉防止重复提交的问题,而前端的业务开发不受影响,不改代码,无感知. 我想架构的目的之一,

  • JavaScript使用promise处理多重复请求

    一.为什么要写这个文章? 处理重复请求的文章想必大家也看过了很多,大多数都是分为在response返回之前发现重复请求就return掉的和使用节流/防抖来间接规避用户频繁操作两种版本的.最近在使用的过程的中,发现这两个版本在某些场景下还是有些局限性. 二.问题场景 如图,我这个h5的页面,顶部和底部都要显示这个名片组件.这些名片的信息是通过一个接口来获取的,当这个组件在当前页面被初始化时,就会发生两次重复的请求. 这时会面临几个抉择: 1. 不对重复请求做任何处理. 缺点1:造成不必要的资源浪费

  • JavaScript使用Promise实现并发请求数限制

    目录 没有Promise的并发请求 使用Promise限制并发请求 使用Promise实现并发请求数限制 总结 没有Promise的并发请求 在Web开发中,我们经常需要发起多个异步请求来获取数据.例如,我们可能需要从服务器获取一些用户信息.文章内容.评论列表等等.如果我们使用的是传统的JavaScript回调函数,可能会写出类似下面这样的代码: function getUsers(callback) { fetch('https://example.com/users', (response)

  • 理解JavaScript中Promise的使用

    Javascript 采用回调函数(callback)来处理异步编程.从同步编程到异步回调编程有一个适应的过程,但是如果出现多层回调嵌套,也就是我们常说的厄运的回调金字塔(Pyramid of Doom),绝对是一种糟糕的编程体验.于是便有了 CommonJS 的 Promises/A 规范,用于解决回调金字塔问题.本文先介绍 Promises 相关规范,然后再通过解读一个迷你的 Promises 以加深理解. 什么是 Promise 一个 Promise 对象代表一个目前还不可用,但是在未来的

  • 自定义事件解决重复请求BUG的问题

    现在,组件化开发还是比较流行的,毕竟其优点相当突出.最近在开发一个组件的时候,遇到了一个很有意思的BUG... BUG的背景 最近在开发一个组件,好不容易开发好了转测试.然后,测试给我提了一个这样的bug,orz... 因为是一个组件,最大的好处就是可以随处复用,随处使用,然而,当一个页面用了多个组件,只有最后一个生效的时候,这个组件就没有什么意义了... BUG原因查找 这个组件的初始数据来源的接口是固定的,也就是说,页面内的所有这个组件在初始化的时候都会发出同样的请求,这里的请求是jsonp

  • 项目中如何使用axios过滤多次重复请求详解

    目录 一.前言: 这个情况下,我们通常的做法有两种: 二.CancelToken类 最终效果 总结 一.前言: 我们在web应用开发过程当中,经常会遇到一个时刻发起了多个请求的场景 这个情况下,我们通常的做法有两种: 可以在请求时show一个loading,阻止用户操作. 或者人为加个变量,做一个请求的节流 我们的项目中,目前大部分情况也是采用以上两种方式做的.今天来介绍一个新的方式. 二.CancelToken类 我们之前实例化一个Promise,这个对象是否成功与否,是无法在函数外部决定的,

  • axios取消请求与避免重复请求

    目录 起源 现状 取消请求 cancelToken 修改后的请求方法 避免重复请求 总结 起源 某个页面需要下载全部数据的功能,下载数据量大,接口延迟长..... 某个页面加载初始数据量延长长,但单个检索快速,出现初始数据加载中时,检索接口返回,初始数据后续返回覆盖了检索数据的展示.... 这些情况需要我们: 能够手动取消/终止请求Request. 某些页面接口同时只能有一个在请求. 现状 系统基于老哥花裤衩开源的vue-element-admin做的二次开发,其中的请求采用的是axios,其中

  • Axios取消重复请求的方法实例详解

    目录 前言 一.如何取消请求 二.如何判断重复请求 三.如何取消重复请求 3.1 定义辅助函数 3.2 设置请求拦截器 3.3 设置响应拦截器 四.CancelToken 的工作原理 五.总结 六.参考资源 前言 在 Web 项目开发过程中,我们经常会遇到重复请求的场景,如果系统不对重复的请求进行处理,则可能会导致系统出现各种问题.比如重复的 post 请求可能会导致服务端产生两笔记录.那么重复请求是如何产生的呢?这里我们举 2 个常见的场景: 假设页面中有一个按钮,用户点击按钮后会发起一个 A

  • 浅谈Axios去除重复请求方案

    目录 一.取消重复请求 二.清理所有请求 此方案主要有两个功能 1.请求发出后,后续重复请求取消不处理,等待第一次请求完成. 2.路由跳转后,上一个页面未完成请求全部清理. 一.取消重复请求 前置知识: 1.axios官方提供的取消方法,可以查阅相关文档:CancelToken 2.js Map相关概念 3.安全的查询字符串解析和字符串分解库 qs,功能类似js自带的JSON 为简化参数处理,本方案只考虑post请求,也就是如果method,url以及data相同则视为重复请求 // axios

  • 浅谈axios中取消请求及阻止重复请求的方法

    目录 前言 核心--CancelToken 实际应用和封装 一些小细节 前言 在实际项目中,我们可能需要对请求进行"防抖"处理.这里主要是为了阻止用户在某些情况下短时间内重复点击某个按钮,导致前端向后端重复发送多次请求.这里我列举两种比较常见的实际情况: PC端 - 用户双击搜索按钮,可能会触发两次搜索请求 移动端 - 因移动端没有点击延迟,所以极易造成误操作或多操作,造成请求重发 以上情况有可能在有Loading遮罩时依然发生,所以我们要考虑前端阻止重复请求的方法. 核心--Canc

  • vue axios拦截器常用之重复请求取消

    引言 上一篇介绍了axios的简单封装,知道了axios拦截器的应用场景和方法,今天来看一下对于响应时间过长且请求次数过高的情况拦截器如何处理. 取消请求的方法 Axios使用内部提供的CancelToken来取消请求 官网示例1:用CancelToken.source工厂方法创建 cancel token,像这样 const CancelToken = axios.CancelToken; const source = CancelToken.source(); axios.get('/use

随机推荐