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

发布于 2022-09-06 02:20:38 字数 10191 浏览 19 评论 0

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

1、websocket服务部分


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服务部分

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、页面逻辑(用户可以操作增加工作经验列表)

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页面部分

<ion-model-view>
  <ion-header-bar class="text-center">
    <button ng-if="!work.isAdd" ng-bind="i18n.cancel_label" ng-click="work.cancelEdit()"
            class="button button-clear button-calm"></button>
    <h1 ng-bind="work.operatingTitle" class="title"></h1>
  </ion-header-bar>
  <ion-content>
    <div class="list">
      <div ng-repeat="item in work.cardInfo track by $index" class="item item-input-inset">
        <span ng-bind="item.text" class="input-label w25p"></span>
        <input type="text" ng-if="item.isInput" ng-model="item.value">
        <span ng-if="item.isDateSelect" ng-bind="item.value"></span>
        <i ng-if="item.isDateSelect" ng-click="work.selectDate(item)" class="icon ion-edit icon-pos-right"></i>
        <textarea ng-if="item.isTextArea" ng-model="item.value" cols="40" rows="4"></textarea>
      </div>
      <div ng-if="work.isAdd" class="button-bar">
        <a ng-bind="i18n.cancel_label" ng-click="close()" class="button button-stable leftBtnRadius"></a>
        <a ng-bind="i18n.add_btn" ng-click="work.addExperience()" class="button button-calm rightBtnRadius"></a>
      </div>
    </div>
  </ion-content>
</ion-model-view>

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

枯寂 2022-09-13 02:20:38

getWorkExperienceList的这个Observable直接加一个初始值然后把初始化的逻辑写到do就可以了吧。

而且管理Observable使用内置提供的操作符更好吧,直接在一个Observerdispatch另一个,看着有点混乱啊。

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文