最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 如何解决异步接口请求快慢不均导致的数据错误问题? - DevUI

    正文概述 掘金(DevUI团队)   2021-05-08   528

    DevUI 是一款面向企业中后台产品的开源前端解决方案,它倡导沉浸灵活至简的设计价值观,提倡设计者为真实的需求服务,为多数人的设计,拒绝哗众取宠、取悦眼球的设计。如果你正在开发 ToB工具类产品,DevUI 将是一个很不错的选择!

    如何解决异步接口请求快慢不均导致的数据错误问题? - DevUI

    引言

    搜索功能,我想很多业务都会涉及,这个功能的特点是:

    • 用户可以在输入框中输入一个关键字,然后在一个列表中显示该关键字对应的数据;
    • 输入框是可以随时修改/删除全部或部分关键字的;
    • 如果是实时搜索?(即输入完关键字马上出结果,不需要额外的操作或过多的等待),接口调用将会非常频繁。

    实时搜索都会面临一个通用的问题,就是:

    问题重现

    最近测试提了一个搜索(PS:此处的搜索?就是用 DevUI 新推出的 CategorySearch 组件实现的)相关的缺陷单,就涉及到了上述问题。

    如何解决异步接口请求快慢不均导致的数据错误问题? - DevUI

    这个bug单大致意思是:

    从缺陷单的截图来看,本意是要搜索关键字8.4.7迭代】,表格中的实际搜索结果是8.4.7迭代】过关键字的数据。

    缺陷单的截图还非常贴心地贴了两次请求的信息:

    如何解决异步接口请求快慢不均导致的数据错误问题? - DevUI

    作为一名“有经验的”前端开发,一看就是一个通用的技术问题:

    1. 浏览器从服务器发起的请求都是异步的;
    2. 由于前一次请求服务器返回比较慢,还没等第一次请求返回结果,后一次请求就发起了,并且迅速返回了结果,这时表格肯定显示后一次的结果;
    3. 过了2秒,第一次请求的结果才慢吞吞地返回了,这时表格错误地又显示了第一次请求的结果;
    4. 最终导致了这个bug。

    怎么解决呢?

    在想解决方案之前,得想办法必现这个问题,靠后台接口是不现实的,大部分情况下后台接口都会很快返回结果。

    所以要必现这个问题,得先模拟慢接口。

    模拟慢接口

    为了快速搭建一个后台服务,并模拟慢接口,我们选择 Koa 这个轻量的 Node 框架。

    快速开始

    Koa 使用起来非常方便,只需要:

    1. 新建项目文件夹:mkdir koa-server
    2. 创建 package.json:npm init -y
    3. 安装 Koa:npm i koa
    4. 编写服务代码:vi app.js
    5. 启动:node app.js
    6. 访问:http://localhost:3000/

    编写服务代码

    使用以下命令创建 app.js 启动文件:

    vi app.js
    

    在文件中输入以下 3 行代码,即可启动一个 Koa 服务:

    const Koa = require('koa'); // 引入 Koa
    const app = new Koa(); // 创建 Koa 实例
    app.listen(3000); // 监听 3000 端口
    

    访问

    如果没有在3000端口启动任务服务,在浏览器访问:

    http://localhost:3000/

    会显示以下页面:

    如何解决异步接口请求快慢不均导致的数据错误问题? - DevUI

    启动了我们的 Koa Server 之后,访问:

    http://localhost:3000/

    会显示:

    如何解决异步接口请求快慢不均导致的数据错误问题? - DevUI

    get 请求

    刚才搭建的只是一个空服务,什么路由都没有,所以显示了Not Found

    我们可以通过中间件的方式,让我们的 Koa Server 显示点儿东西。

    由于要增加一个根路由,我们先安装路由依赖

    npm i koa-router
    

    然后引入 Koa Router

    const router = require('koa-router')();
    

    接着是编写get接口

    router.get('/', async (ctx, next) => {
      ctx.response.body = '<p>Hello Koa Server!</p>';
    });
    

    最后别忘了使用路由中间件

    app.use(router.routes());
    

    改完代码需要重启 Koa 服务,为了方便重启,我们使用 pm2 这个 Node 进程管理工具来启动/重启 Koa 服务,使用起来也非常简单:

    • 全局安装 pm2:npm i -g pm2
    • 启动 Koa Server:pm2 start app.js
    • 重启 Koa Server:pm2 restart app.js

    重启完 Koa Server,再次访问

    http://localhost:3000/

    会显示以下内容:

    如何解决异步接口请求快慢不均导致的数据错误问题? - DevUI

    post 请求

    有了以上基础,就可以写一个 post 接口,模拟慢接口啦!

    编写 post 接口和 get 接口很类似:

    router.post('/getList', async (ctx, next) => {
      ctx.response.body = {
        status: 200,
        msg: '这是post接口返回的测试数据',
        data: [1, 2, 3]
      };
    });
    

    这时我们可以使用 Postman 调用下这个 post 接口,如期返回:

    如何解决异步接口请求快慢不均导致的数据错误问题? - DevUI

    允许跨域

    我们尝试在 NG CLI 项目里调用这个 post 接口:

    this.http.post('http://localhost:3000/getList', {
      id: 1,
    }).subscribe(result => {
      console.log('result:', result);
    });
    

    但是在浏览器里直接调用,却得不到想要的结果:

    • result 没有打印出来
    • 控制台报错
    • Network请求也是红色的

    如何解决异步接口请求快慢不均导致的数据错误问题? - DevUI

    由于本地启动的项目端口号(4200)和 Koa Server 的(3000)不同,浏览器认为这个接口跨域,因此拦截了。

    NG CLI 项目本地链接:

    http://localhost:4200/

    Koa Server 链接:

    http://localhost:3000/

    Koa 有一个中间件可以允许跨域:koa2-cors

    这个中间件的使用方式,和路由中间件很类似。

    先安装依赖:

    npm i koa2-cors
    

    然后引入:

    const cors = require('koa2-cors');
    

    再使用中间件:

    app.use(cors());
    

    这时我们再去访问:

    http://localhost:4200/

    就能得到想要的结果啦!

    如何解决异步接口请求快慢不均导致的数据错误问题? - DevUI

    慢接口

    post 接口已经有了,怎么模拟慢接口呢?

    其实就是希望服务器延迟返回结果。

    在 post 接口之前增加延迟的逻辑:

      async function delay(time) {
        return new Promise(function(resolve, reject) { 
          setTimeout(function() {
            resolve();
          }, time);
        });
      }
    
      await delay(5000); // 延迟 5s 返回结果
    
      ctx.response.body = { ... };
    

    再次访问 getList 接口,发现前面接口会一直pending,5s 多才真正返回结果。

    如何解决异步接口请求快慢不均导致的数据错误问题? - DevUI

    如何解决异步接口请求快慢不均导致的数据错误问题? - DevUI

    取消慢接口请求

    能模拟慢接口,就能轻易地必现测试提的问题啦!

    这是修复bug正确的打开方式。

    最直观的方案就是再发起第二次请求之后,如果第一次请求未返回,那就直接取消这次请求,使用第二次请求的返回结果。

    怎么取消一次http请求呢?

    Angular 的异步事件机制是基于 RxJS 的,取消一个正在执行的 http 请求非常方便。

    前面已经看到 Angular 使用 HttpClient 服务来发起 http 请求,并调用subscribe 方法来订阅后台的返回结果:

    this.http.post('http://localhost:3000/getList', {
      id: 1,
    }).subscribe(result => {
      console.log('result:', result);
    });
    

    要取消 http 请求,我们需要先把这个订阅存到组件一个变量里:

    private getListSubscription: Subscription;
    
    this.getListSubscription = this.http.post('http://localhost:3000/getList', {
      id: 1,
    }).subscribe(result => {
      console.log('result:', result);
    });
    

    然后在重新发起 http 请求之前,取消上一次请求的订阅即可。

    this.getListSubscription?.unsubscribe(); // 重新发起 http 请求之前,取消上一次请求的订阅
    
    this.getListSubscription = this.http.post(...);
    

    其他 http 库如何取消请求

    至此这个缺陷算是解决了,其实这是一个通用的问题,不管是在什么业务,使用什么框架,都会遇到异步接口慢导致的数据错乱问题。

    那么,如果使用 fetch 这种浏览器原生的 http 请求接口或者 axios 这种业界广泛使用的 http 库,怎么取消正在进行的 http 请求呢?

    fetch

    先来看下 fetch,fetch 是浏览器原生提供的 AJAX 接口,使用起来也非常方便。

    使用 fetch 发起一个 post 请求:

    fetch('http://localhost:3000/getList', {
       method: 'POST',
      headers: {
        'Content-Type': 'application/json;charset=utf-8'
      },
      body: JSON.stringify({
        id: 1
      })
    }).then(result => {
      console.log('result', result);
    });
    

    可以使用 AbortController 来实现请求取消:

    this.controller?.abort(); // 重新发起 http 请求之前,取消上一次请求
    
    const controller = new AbortController(); //  创建 AbortController 实例
    const signal = controller.signal;
    this.controller = controller;
    
    fetch('http://localhost:3000/getList', {
       method: 'POST',
      headers: {
        'Content-Type': 'application/json;charset=utf-8'
      },
      body: JSON.stringify({
        id: 1
      }),
      signal, // 信号参数,用来控制 http 请求的执行
    }).then(result => {
      console.log('result', result);
    });
    

    axios

    再来看看 axios,先看下如何使用 axios 发起 post 请求。

    先安装:

    npm i axios
    

    再引入:

    import axios from 'axios';
    

    发起 post 请求:

    axios.post('http://localhost:3000/getList', {
      headers: {
        'Content-Type': 'application/json;charset=utf-8'
      },
      data: {
        id: 1,
      },
    })
    .then(result => {
      console.log('result:', result);
    });
    

    axios 发起的请求可以通过 cancelToken 来取消。

    this.source?.cancel('The request is canceled!');
    
    this.source = axios.CancelToken.source(); // 初始化 source 对象
    
    axios.post('http://localhost:3000/getList', {
      headers: {
        'Content-Type': 'application/json;charset=utf-8'
      },
      data: {
        id: 1,
      },
    }, { // 注意是第三个参数
      cancelToken: this.source.token, // 这里声明的 cancelToken 其实相当于是一个标记或者信号
    })
    .then(result => {
      console.log('result:', result);
    });
    

    小结

    本文通过实际项目中遇到的问题,总结缺陷分析和解决的通用方法,并对异步接口请求导致的数据错误问题进行了深入的解析。

    加入我们

    我们是DevUI团队,欢迎来这里和我们一起打造优雅高效的人机设计/研发体系。招聘邮箱:muyang2@huawei.com。

    文/DevUI Kagol

    往期文章推荐

    《号外号外!DevUI Admin V1.0 发布啦!》

    《让我们一起建设 Vue DevUI 项目吧!? 》

    《CategorySearch分类搜索组件初体验》

    《对DevUI组件库王哥的专访》

    《2021年最值得推荐的7个Angular前端组件库》


    起源地下载网 » 如何解决异步接口请求快慢不均导致的数据错误问题? - DevUI

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元