为了账号安全,请及时绑定邮箱和手机立即绑定

js异步ajax实现避免页面重复提交

标签:
JavaScript

1 ajax概述

ajax同步与异步区别,在于:

  • 同步基本是秉持着顺序执行的,为此在传送数据期间,浏览器一直处于等待状态,如果数据量过多或者网速过慢,就会出现假死情况(此内容在笔者此前博文<异步ajax实现顺序执行>中也有提出),所以当代ajax已经基本不再使用同步配置了;

  • 异步则是发送后,接下来该干什么干什么,类似于行军途中,不断派出的斥候与正常军队行进之间的关系.只是有时候有些斥候的信息比较重要,在军队或下一个斥候派出前,必须得到这些斥候的信息,才好往下走.于此衍生了各种可能存在的问题,其中一个新人常遇到的问题就是:连续触发相同事件导致的重复提交.


2 问题描述

需要说明的是,重复提交对于不同情况有着不同的解决策略.
本文所述的方法,主要针对的是正常使用情况下,用户对于相同事件的误重复触发,譬如对于一个按钮,由于鼠标不好使或者新用户的不了解,连续点击两次以上.这种情况下,从页面js设计就可以解决;
与之正常使用情况对应的,即是异常情况,如某些用户恶意频繁发送请求,以获取一个网站的用户登录信息,这类问题就必然要和后台互动才能解决了,不在本文讨论范围内;
以下分情况讨论了可能出现的问题:

  • 对于get请求,往往不会有什么太恶劣的影响,最多只是浪费下网络带宽,稍微增加服务器压力罢了,譬如查询两次相同的商品列表,仅是浪费时间;

  • 对于post请求,重复触发很可能就产生用户不想要的某种后果,譬如点击通过积分购买商品,由于第一次网络较差导致反应时间较长,但用户以为自己第一次没有点击,于是再次点击,就相当于又发了一次post请求,最后的结果,就是买了两件相同的商品,这用户体验就很差了;


3 思路描述

ajax连续触发相同事件导致的重复提交,有一点可以利用的是:提交时的url必定相同.
所以只需将封装的ajax方法在发送请求之前,使用js变量存储该url,而在接收完相应数据后,再将该变量标记移除.
误操作导致要进行第二次ajax请求时,查询已有的js变量,如果变量标记存在,则说明第一次相应还没有回来,则直接禁止发送第二次相同的请求.
如此,即可避免ajax重复请求了.


4 封装代码

以下是封装ajax方法的代码(至少需要jQuery支持,如果有layer支持,则会使显示优化,客户体验更好):

/**
 * 参数说明:
 * 1.url(必填)
 * 2.config(选填,各种参数的配置)
 * 		2.1.type:默认post
 * 		2.2.async:默认true
 * 		2.3.cache:默认false
 * 		2.4.dataType:默认json
 * 		2.5.contentType:默认utf8
 * 		2.6.data:默认无参数,此处一般需要使用者手动添加
 * 		2.7.packRemindType:默认:有layer插件时优先layer提示,否则为console提示.'console',控制台提示;'layer',layer提示;其他方式看需求增加,仅在函数值无效时有效
 * 3.func_suc(选填):即ajax中的success
 * 4.func_error(选填):即ajax中的error
 * 5.func_comp(选填):即ajax中的complete
 * 其他说明
 * 1.防止重复提交(自动启用,但在同步加载模式下看不出效果);
 * 2.提示模式,参考packRemindType;
 */function ajax_pack(url,config,func_suc,func_error,func_comp){	//1.request url repeat judge
	if(!window.lstAjaxUrl){		window.lstAjaxUrl={};
	}	if(window.lstAjaxUrl[url]){		console.log('ajax (url:%s) submit repeat!',url);//重复提示在控制台
		return;
	}else{		window.lstAjaxUrl[url]=true;
	}	//2.init necessary param
	config=config||{};
	config.type=config.type||'post';//默认为type=get
	config.async=config.async||true;//默认为同步加载(选择此参数时,是没有必要用防重复提交功能的)
	config.cache=config.cache||false;//默认禁用缓存
	config.dataType=config.dataType||'json';
	config.contentType=config.contentType||'application/x-www-form-urlencoded; charset=utf-8';
	config.data=config.data||{};	
	var packRemindTypeDefault=typeof(layer)==='undefined'?'console':'layer';//有layer时优先取layer
	config.packRemindType=config.packRemindType||packRemindTypeDefault;//默认:'console',控制台提示;'layer',layer提示;其他方式看需求增加,仅在函数值无效时有效
	
	var func_send;//before send,not in param
	
	if(config.packRemindType==='console'){
		func_send=function(){};
		func_suc=func_suc||function(data){console.log(data);};
		func_error=func_error||function(){console.log(url+' error');};
		func_comp=func_comp||function(){};
	}else if(config.packRemindType==='layer'){//需要layer支持
		if(!window.lstLayerLoad){			window.lstLayerLoad={};
		}
		func_send=function(){			window.lstLayerLoad[url]=layer.msg('加载中,请稍后...',{time:0});//layer.load(0, {shade: false,time:0}); //0代表加载的风格,支持0-2
		};		var func_suc_init=func_suc||function(data){			if(data.success==1){
				layer.msg(data.message,{icon:1});
			}else{
				layer.msg(data.message,{icon:2});
			}
		};
		func_suc=function(data){
			layer.close(window.lstLayerLoad[url]);
			func_suc_init(data);
		};		var func_error_init=func_error||function(){
			layer.msg('网络错误,请稍后重试!',{icon:2});
		};
		func_error=function(){
			layer.close(window.lstLayerLoad[url]);
			func_error_init();
		};
		func_comp=func_comp||function(){};
	}else{		console.log('nonsupport packRemindType.');		return;
	}	//3.get param
	$.ajax({
		url:url,
		type:config.type,		async:config.async,
		cache:config.cache,
		dataType:config.dataType,
		contentType:config.contentType,
		data:config.data,
		beforeSend:function(){
			func_send();
		},
		success:function(data){			if(!window.lstAjaxResult){				window.lstAjaxResult={};
			}			window.lstAjaxResult[url]=data;//封装结果
			func_suc(data);
		},
		error:function(){
			func_error();
		},
		complete:function(data){			/*
			var mResult;
			if(config.dataType==='json'){
				mResult=JSON.parse(data.responseText);
			}else{
				mResult=data.responseText;
			}
			 */
			func_comp();			window.lstAjaxUrl[url]=false;//释放url锁
		}
	});
}

以上封装代码中的很多默认值是根据笔者公司的倾向确定的,使用者也可根据自己需要予以修改(只要是这个思路就可以). 以下为使用该封装函数的代码,其中url为假象url,就不用真实url替代了.

//某个vue methods中方法内容var this=that;ajax_pack(mGlobalUrl.loadOne,{	type:'get',
	data:{		strFormCode:strFormCode
	}},function(data){	that.mainData=Object.assign(that.mainData,data);
});

作者:社哥

来源:https://my.oschina.net/yangyishe/blog/1795196

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消