一、同步和異步
在前端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是從這個公共接口請求返回的,也就是說必須先等待公共接口執行完畢,再返回。這種回調函數的方式,既保證了調用先後順序,又能保證耦合度降低。