一、同步和异步
在前端JavaScript的应用中,如果要代码指定顺序执行,就不得不提到同步和异步了。先来讲讲什么是同步,什么是异步。按个人理解,异步就是不同的路,不同的路同时执行,互不影响,没有先后顺序,谁速度快,谁就先执行完。同步就是相同的路,同一条路只能有一个在执行,必须有先后顺序,先执行完一个,才能执行后面那个。ajax请求,默认是异步请求。多个ajax请求可以同时发起,执行顺序不定。适用于互不影响的场景。也可以将请求设置为同步的,那么在ajax请求未执行完成之前,页面会挂起,等待执行完成后,才会继续执行其他的代码。适用于存在逻辑顺序的场景,比如第二个请求的参数,来源于第一个请求的结果。
二、回调函数
关于回调函数的理解,可以参考彻底理解JavaScript中回调函数 (推荐)
基于上一点中提到的先后顺序的问题,另外还有一种情况也能达到效果,那就是回调函数。ajax有success成功的回调函数,将第二个请求放在第一个ajax的success中执行,也能保证顺序,这是最常见回调函数。
如果有几个请求,每个请求都要依赖上一个请求的返回结果,全部都写在回调函数success中,会怎么样呢?代码是不是很庞大,可阅读性会大大降低,那该怎么做呢。
再比如,当前有一个场景,后台接口提供了一个公共的查询方法,根据参数类型可以查询不同的值域集合返回到前端。在前端在根据返回的结果生成html代码填充到页面上。如果值域类型很多,为每个类型都写一个ajax请求,那会使得效率低下。如果,只写一个ajax请求,在回调函数中,根据不同的参数做不同的处理,那会使得代码耦合度提高。假使后面有场景会大量调用该方法,会影响性能。
使用自定义的回调函数能解决这个问题,具体业务场景:
说明:
1、当前页面数据是动态生成的,生成的页面。项目栏是下拉框,每次动态生成都要重新获取值域列表。
2、当前页面有很多操作都会动态生成页面数据,右上角的新增,导入,页面初始化,从缓存中获取数据等,每个场景业务逻辑不同,调用场景较多。
3、请求值域的接口是公共的,除了当前页面的项目,还有很多其他的值域请求,根据参数编码区分。
前端公共方法:
/** *重新加载下拉框数据 type checkType 质控类型 checkGroup检查小组 formClassify 表单分类 itemList 项目列表 */ function reloadQualityCheckList(type,callback){ var itemListHtml=""; $.ajax({ type:"POST", url:basePath+"/qualityCheckPlan/qualityCheckReloadData.do", //async:false, data:{ type:type }, success:function(msg){ if(msg.state == "3"){ //表单分类数据处理 if("formClassify"==type){ var formClassifyListHtml="<div class=\"item\" data-value=\"\" style=\"line-height:15px;\">请选择表单分类</div>"; var formClassifyList=msg.formClassifyList; //获取原来的数据 var formClassify=$("#pro").val(); var count=0; if(formClassifyList){ for(var i=0;i<formClassifyList.length;i++){ formClassifyListHtml+="<div class=\"item\" data-value=\""+formClassifyList[i].id+"\" style=\"line-height:15px;\">"+formClassifyList[i].name+"</div>"; if(formClassify==formClassifyList[i].id){ count++; } } } $("#qualityCheckContentClassifyList").html(formClassifyListHtml); if(count==0){ //判断原来的选项值域 的值是否被删除 删除了则清空选项 $("#pro").parent().dropdown("clear"); } //项目列表 }else if("itemList"==type){ var itemList=msg.itemBaseList; for(var i=0;i<itemList.length;i++){ //项目 var itemBase=itemList[i]; //分数 var target_score=itemBase.target_score; if(formType==1 && target_score>0){ itemListHtml+=" <div class=\"item\" data-value=\""+itemBase.user_id+"\" style=\"border-bottom-style: none;\">"+itemBase.item_name+"</div>"; //如果是填空制,只取分数等于0的项目 }else if(formType==2 && target_score==0){ itemListHtml+=" <div class=\"item\" data-value=\""+itemBase.user_id+"\" style=\"border-bottom-style: none;\">"+itemBase.item_name+"</div>"; } } if(callback){ callback(itemListHtml); } } } }, error:function(){ msgError(); } }); return itemListHtml; }
当前请求是项目列表类型的
该公共请求的目的是将值域列表的内容查询返回到界面,再对数据进行处理。每个类型的处理是不同的,有的直接调用jquery改变值,有的要生成html返回。而同一个类型返回html的内容也可能是不相同的。如果将这些逻辑全部都写到回调函数里面,代码就会变得难以阅读。如果将不同的逻辑封装成回调函数,通过参数传进来。就会好很多。此例子仅处理了项目类型的回调函数封装。
调用处示例:
1、行点击时调用。此处是将值域代码html返回,添加空行。
2、初始进入,没有任何数据时的初始化下拉框
3、默认加载数据调用,加载数据逻辑极为复杂。
4、从库中导入,初始化页面数据。导入逻辑也较为复杂。
...还有很多,就不一一列举了
调用函数解析:
function reloadQualityCheckList(type,callback) 传入一个类型和一个回调函数,当前使用的是itemList类型。对应部分的回调函数逻辑如下
将返回的html拼装html,再将html传入回调函数中。以调用示例1为例,将返回的html拼装到完整的行html中,最后添加到最后一行的后面。
具体调用逻辑,不同的场景是不同的,复杂度也不一样。
总结:
综上所述,每个调用处,都有较为复杂的逻辑,且复杂逻辑中有很多数据来源调用处的场景。将这些又庞大又复杂的代码,还有复杂逻辑中需要的全部参数全部写到reloadQualityCheckList的ajax请求success回调函数中,根本不现实。而且必须要保证返回的html是从这个公共接口请求返回的,也就是说必须先等待公共接口执行完毕,再返回。这种回调函数的方式,既保证了调用先后顺序,又能保证耦合度降低。
发表评论