在开发过程中,可能会遇到用户连续操作,而造成请求重复的问题(比如用户添加工作经验列表时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页面部分
用户在4、html部分中点击了添加工作经验列表的按钮后 ,触发了addExperience()事件,然后在3、页逻辑中,addExperience事件先表单验证通过后,再调用2、api服务中的getWorkExperienceAdd方法,将参数传递到2、api中的get方法,get方法中调用1、websocket服务中的get方法,发出websocket请求。
用户点击的时候,考虑到用户可能重复点击多次,造成添加几次重复的请求,这个时候想用Rxjs的debounceTime,switchMap来过滤 请求,只保留用户一次操作。同时又不能影响在页面中,进入页面中就调用了 apiService.getWorkExperienceList查询工作经验列表 的调用。想请教一下有什么好的方法?