热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

请教如何用RxJS实现防止因为用户重复操作,发出多次相同请求的问题?debounceTime,switchMap?

在开发过程中,可能会遇到用户连续操作,而造成请求重复的问题(比如用户添加工作经验列表时WorkExperienceAdd可能重复添加),这个时候只需要一次请求即可,考虑到引入了rxjs想结合sw

在开发过程中,可能会遇到用户连续操作,而造成请求重复的问题(比如用户添加工作经验列表时WorkExperienceAdd可能重复添加),这个时候只需要一次请求即可,考虑到引入了rxjs想结合switchMap来实现,但是又不能影响到其他的请求(请求用户的工作经验列表WorkExperienceList),所以想请求一下帮助,有什么比较好的实现?



1、websocket服务部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
angular.module("app").factory("wsService", ["$q", "$ionicLoading", "i18n", "timeService", "commonService", "CONFIG",

  function ($q, $ionicLoading, i18n, timeService, commonService, CONFIG) {

    "use strict";

    const url = CONFIG.url.server;

    let ws = new WebSocket(url),

      arr = [],

      log = [],

      subject = new Rx.Subject();

     

    function reConnect(url) {

      let ws = new WebSocket(url);

      return bridgeWs(ws);

    }



    function send(data) {

      if (data) {

        sendData(data);

      } else {

        _.each(arr, item => {

          sendData(item);

        });

        arr = [];

      }



      function sendData(data) {

        if (data.parameters.showLoading) data.parameters = _.omit(data.parameters, ["showLoading"]);

        ws.send(JSON.stringify(data));

      }

    }



    function createRecord(msg, type) {

      const message = msg + " " + timeService.getTime() + " " + type.toUpperCase();

      log.push(message);

    }





    function message(event) {

      let data = JSON.parse(event.data);

      const criticalCode = 2000;

      data.isError = data.code > criticalCode;

      subject.next(data);

    }



    function close(event) {

      createRecord(event.reason, "close");

    }



    function error() {

      commonService.showShortCenter(i18n.server_error_tip);

      createRecord("connection error ", "error");

      console.error(log.join("\\n"));

    }



    function open() {

      !_.isEmpty(arr) && send();

      createRecord("connection established ", "open");

    }



 

    function bridgeWs(ws) {

      ws.Onmessage= message;

      ws.Onclose= close;

      ws.Onerror= error;

      ws.Onopen= open;

      return ws;

    }



    bridgeWs(ws);



    return {

      record: log,



      get: function (data) {

        console.log("get中的data");

        console.log(data);

        const readyState = 0,

          errorState = 2,

          closeState = 3;

        if (ws.readyState === readyState) {

          arr.push(data);

        } else if (_.includes([errorState, closeState], ws.readyState)) {

          ws.close();

          arr = [];

          arr.push(data);

          //TODO 重连直至连上为止

          ws = reConnect(url);

        } else {

          data.parameters.showLoading && commonService.showLoading();

          send(data);

          return new Date().getTime();

        }

      },



      close: ws.close,



      status: function () {

        return ws.readyState;

      },



      subject: subject

    };

  }]);

2、api服务部分



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
angular.module("app").factory("apiService", ["API", "i18n", "wsService", "$state", "commonService",

  function (API, i18n, wsService, $state, commonService) {

    "use strict";



    const subject = new Rx.Subject(),

      redirectCode = 2101;



    let parts = wsService.subject.partition(data => data.code === redirectCode);



    parts[0].subscribe(() => {

      $state.go("login");

    });



    parts[1].subscribe(data => {

      subject.next(data);

    });



 

    function createErrorSheet(item, record = {}) {

      if (_.isArray(item)) return _.map(item, commonService.curry2Right(_.assign)(record));

      return _.map(_.toPairs(item), ele => {

        let key = _.isArray(ele[1]) ? "key" : "form";

        record[key] = ele[0];

        return createErrorSheet(ele[1], record);

      });

    }



   

    function arrangeErrorInfo(data) {

      if (_.isString(data)) return data;

      return _.chain(data).map(item => createErrorSheet(item)).flattenDeep().value();

    }



    function curry2(fn) {

      return f => s => fn.call(fn, f, s);

    }



    class Command {

      constructor(path, parameters) {

        this.command = {path};

        this.parameters = parameters;

      }

    }



   

    const get = curry2((path, params) => {

      const option = new Command(path, params);

      console.log("get中的option");

      console.log(option);

      wsService.get(option);

    });



    class SubApi extends API {

      constructor() {

        super();

        this.subject = subject;

      }



      getLogin(param) {

        get(this.login)(param);

      }

     

     getWorkExperienceList(param) {

        get(this.workExperienceList)(param);

      }

     

     getWorkExperienceAdd(param) {

        get(this.workExperienceAdd)(param);

      }

     

      handleError(data) {

        commonService.hideLoading();

        const message = arrangeErrorInfo(data.detail),

          tip = _.isString(message) ? _.find([message, `1、${data.msg}`], _.identity) :

            _.map(message, (msg, index) => `${index + 1}、${msg.message}\n`).join("");

        try {

          window.plugins.toast.showLongCenter(tip);

        } catch (e) {

          commonService.alert(i18n.api_error_tip, tip);

        }

      }



     

      isDataOf(path, data) {

        return path === data.command.path;

      }



     

      isDataFor(data, ...path) {

        return _.includes(path, data.command.path);

      }



      predicatePath(path) {

        return data => path === data.command.path;

      }

    }



    return new SubApi();

  }]);

3、页面逻辑(用户可以操作增加工作经验列表)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
angular.module("app").controller("personalCtrl", ["$scope", "apiService", "$state", "commonService", "ionicDatePicker",

  "$ionicPopover", "timeService", "$stateParams", "CONFIG", "$cordovaStatusbar", "validateService",

  function ($scope, apiService, $state, commonService, ionicDatePicker, $ionicPopover, timeService, $stateParams, CONFIG, $cordovaStatusbar, validateService) {

    "use strict";



    const i18n = $scope.i18n,

      current = $stateParams.subState;

    let isDateSelecting = false;



    function selectDate(ele) {

      if (isDateSelecting) return;

      const self = this; //don't fix, use automatic this;

      isDateSelecting = true;

      ionicDatePicker.openDatePicker({

        callback(val) {

          _.isString(ele) && (self.data[ele] = timeService.getDateInfo(val).fullDate);

          _.isObject(ele) && (ele.value = timeService.getDateInfo(val).fullDate);

          isDateSelecting = false;

        },

        cancel() {

          isDateSelecting = false;

        }

      });

    }



    const subject = new Rx.Subject(),

      apiServiceSubscription = apiService.subject.subscribe(data => {

        if (data.isError) {

          apiService.handleError(data);

        } else {

          subject.next(data);

        }

      });

   

    subject.filter(data => apiService.isDataOf(apiService.workExperienceList, data)).subscribe(() => {

      $scope[current].dealData(data.data);

    });



 subject.filter(data => apiService.isDataOf(apiService.workExperienceAdd, data)).subscribe(() => {

       $scope.editable = false;

      $scope.popover && $scope.popover.isShown() && $scope.popover.hide();

      $scope[current].queryData();

      $scope[current].clearCardInfo && $scope[current].clearCardInfo();

      $scope.$digest();

    });





 $scope.work = {

      title: "工作经验",

     

      platformData: {

        data: null,

        isShow: true

      },

      addData: {

        data: null,

        isShow: false

      },

     

      queryData() {

        const option = {

          sid: $scope.userInfo.sid,

          self: 1

        };

        apiService.getWorkExperienceList(option);  **// 查询工作经验**

      },





      addExperience() {

        const option = this.getOption();

        if (this.validateUserInput(option)) {

          apiService.getWorkExperienceAdd({       **// 增加工作经验**    

            sid: $scope.userInfo.sid,

            work_exper_form: option

          });

        }

      },



      validateUserInput() {

        return _.every(this.cardInfo, card => card.validateFn(card));

      },

   

      getOption() {

        return _.reduce(this.cardInfo, (accumlator, card) => {

          accumlator[card.key] = card.value;

          return accumlator;

        }, {});

      },



      init() {

        $state.go("home.personal.work");

        $scope.showAdd = true;

        this.queryData();

      }

    };

   

    $scope[current].init();



    $scope.$on("$ionicView.beforeLeave", () => {

      apiServiceSubscription.unsubscribe();

    });

  }]);

4、html页面部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22


 

   

   



 


 

   


     


       

       

       

       

       

     


     


       

       

     


   


 



用户在4、html部分中点击了添加工作经验列表的按钮后 ,触发了addExperience()事件,然后在3、页逻辑中,addExperience事件先表单验证通过后,再调用2、api服务中的getWorkExperienceAdd方法,将参数传递到2、api中的get方法,get方法中调用1、websocket服务中的get方法,发出websocket请求。

用户点击的时候,考虑到用户可能重复点击多次,造成添加几次重复的请求,这个时候想用Rxjs的debounceTime,switchMap来过滤 请求,只保留用户一次操作。同时又不能影响在页面中,进入页面中就调用了 apiService.getWorkExperienceList查询工作经验列表 的调用。想请教一下有什么好的方法?


   



推荐阅读
  • 使用nodejs爬取b站番剧数据,计算最佳追番推荐
    本文介绍了如何使用nodejs爬取b站番剧数据,并通过计算得出最佳追番推荐。通过调用相关接口获取番剧数据和评分数据,以及使用相应的算法进行计算。该方法可以帮助用户找到适合自己的番剧进行观看。 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • uniapp开发H5解决跨域问题的两种代理方法
    本文介绍了uniapp开发H5解决跨域问题的两种代理方法,分别是在manifest.json文件和vue.config.js文件中设置代理。通过设置代理根域名和配置路径别名,可以实现H5页面的跨域访问。同时还介绍了如何开启内网穿透,让外网的人可以访问到本地调试的H5页面。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • Android JSON基础,音视频开发进阶指南目录
    Array里面的对象数据是有序的,json字符串最外层是方括号的,方括号:[]解析jsonArray代码try{json字符串最外层是 ... [详细]
  • 本文介绍了一种解析GRE报文长度的方法,通过分析GRE报文头中的标志位来计算报文长度。具体实现步骤包括获取GRE报文头指针、提取标志位、计算报文长度等。该方法可以帮助用户准确地获取GRE报文的长度信息。 ... [详细]
  • 本文讨论了Kotlin中扩展函数的一些惯用用法以及其合理性。作者认为在某些情况下,定义扩展函数没有意义,但官方的编码约定支持这种方式。文章还介绍了在类之外定义扩展函数的具体用法,并讨论了避免使用扩展函数的边缘情况。作者提出了对于扩展函数的合理性的质疑,并给出了自己的反驳。最后,文章强调了在编写Kotlin代码时可以自由地使用扩展函数的重要性。 ... [详细]
  • 开发笔记:实验7的文件读写操作
    本文介绍了使用C++的ofstream和ifstream类进行文件读写操作的方法,包括创建文件、写入文件和读取文件的过程。同时还介绍了如何判断文件是否成功打开和关闭文件的方法。通过本文的学习,读者可以了解如何在C++中进行文件读写操作。 ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • 本文介绍了在CentOS上安装Python2.7.2的详细步骤,包括下载、解压、编译和安装等操作。同时提供了一些注意事项,以及测试安装是否成功的方法。 ... [详细]
  • 本文介绍了在处理不规则数据时如何使用Python自动提取文本中的时间日期,包括使用dateutil.parser模块统一日期字符串格式和使用datefinder模块提取日期。同时,还介绍了一段使用正则表达式的代码,可以支持中文日期和一些特殊的时间识别,例如'2012年12月12日'、'3小时前'、'在2012/12/13哈哈'等。 ... [详细]
  • React项目中运用React技巧解决实际问题的总结
    本文总结了在React项目中如何运用React技巧解决一些实际问题,包括取消请求和页面卸载的关联,利用useEffect和AbortController等技术实现请求的取消。文章中的代码是简化后的例子,但思想是相通的。 ... [详细]
  • 如何查询zone下的表的信息
    本文介绍了如何通过TcaplusDB知识库查询zone下的表的信息。包括请求地址、GET请求参数说明、返回参数说明等内容。通过curl方法发起请求,并提供了请求示例。 ... [详细]
  • 模板引擎StringTemplate的使用方法和特点
    本文介绍了模板引擎StringTemplate的使用方法和特点,包括强制Model和View的分离、Lazy-Evaluation、Recursive enable等。同时,还介绍了StringTemplate语法中的属性和普通字符的使用方法,并提供了向模板填充属性的示例代码。 ... [详细]
author-avatar
浪漫的美食
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有