JavaScript回調函數的使用心得(同步調用以及解耦合)

2020-12-15 我的華仔部落

一、同步和異步

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

相關焦點

  • 有關JavaScript中回調函數的所有內容!
    persons.map(greet)接受person數組的每一項,並使用每一項作為調用參數來調用函數greet():greet('小智'),greet('王大冶')。有趣的是persons.map(greet)方法接受greet()函數作為參數。
  • 01-JavaScript 調用堆棧
    JavaScript 引擎是一個單線程解析器,而單線程解析器由堆和單一調用棧組成。瀏覽器提供 Web APIs,比如:DOM,AJAX 和 定時器。 本文旨在說明什麼是調用堆棧以及為什麼需要調用棧?對調用棧的理解有助於我們更加清晰的知道 函數的的層次結構和執行順序 在 JavaScript 的引擎中工作方式。
  • 【JavaScript】Promise函數的用法
    javascript
  • 關於JavaScript中的回調看這篇就夠了
    persons.map(greet) 是用另一個函數作為參數的函數,因此被稱為高階函數。❝回調函數作為高階函數的參數,高階函數通過調用回調函數來執行操作。❞重要的是高階函數負責調用回調,並為其提供正確的參數。
  • C語言函數指針之回調函數
    我的理解是:把一段可執行的代碼像參數傳遞那樣傳給其他代碼,而這段代碼會在某個時刻被調用執行,這就叫做回調如果代碼立即被執行就稱為同步回調,如果過後再執行,則稱之為異步回調回調函數就是一個通過調用的函數。
  • 還在用回調函數?快來學習怎麼將回調函數轉為成Promise吧!
    本文只是簡單的以實例講解如何將回調函數轉化為ES6中的Promise,並不會深入分析回調的優缺點,以及Promise和async/await的原理,如果你想了解這些,請關注我。我將在以後的文章中詳細講解這些知識點。
  • 三個案例帶你了解python回調函數
    回調函數: 把函數的指針(地址)作為參數傳遞給另一個函數,當這個指針被用來調用其所指向的函數時,我們就說這是回調函數。回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用於對該事件或條件進行響應。 通俗理解就是: 把一個函數作為參數傳給另一個函數,第一個函數稱為回調函數。這個被傳入的參數其實是函數指針,即指向一個函數的指針(地址)。
  • [翻譯]淺談JavaScript中的高階函數
    高階函數的採用使得JavaScript適合用來做函數式編程。在JavaScript中,高階函數的使用隨處可見。如果你已經用JavaScript寫過一陣子的代碼,那麼你可能已經在不知情的情況下使用過它了。為了完全理解這個概念,你首先要了解什麼是函數式編程以及頭等函數的概念。
  • 重新認識javascript的settimeout和異步
    然後看了一下文章下面的評論,發現5樓和6樓的回答很有道理,主要意思就是說javascript引擎是單線程執行的,while循環那裡執行的時候,settimeout裡面的函數根本沒有執行的機會,這樣while那裡永遠為真,造成死循環。
  • JS異步編程,回調函數與promise
    他們均使用回調函數來進行異步調用。當回調函數中嵌套了回調函數,甚至是多層回調時,編碼就不夠直觀了。而使用Promise就能通過同步的編碼方式實現異步調用。1.多層回調:使用setTimeout()函數執行3層嵌套的異步回調,編碼不直觀  1   function async(){   2  setTimeout(function(){  //回調函數1   3  console.log(1);    4  setTimeout(function(){  //回調函數2
  • JavaScript 執行機制
    image.png導圖要表達的內容用文字來表述的話:同步和異步任務分別進入不同的執行"場所",同步的進入主線程,異步的進入Event Table並註冊函數。js引擎存在monitoring process進程,會持續不斷的檢查主線程執行棧是否為空,一旦為空,就會去Event Queue那裡檢查是否有等待被調用的函數。
  • JavaScript支持計時事件,如何實現超時調用和間隔調用?
    其實這個功能只需要使用JavaScript中的間隔調用就可以實現,另外,還有超時調用,在設定一段時間之後,就會執行某個函數或某段代碼。能夠在指定時間後執行代碼或重複執行代碼的函數,我們稱為計時事件,在JavaScript中,有2個計時事件,分別為:(1). 超時調用:使用setTimeout()方法表示。(2).
  • JavaScript同步、異步、回調執行順序之經典閉包setTimeout面試題分析
    記住我們的口訣 同步 => 異步 => 回調1、for循環和循環體外部的console是同步的,所以先執行for循環,再執行外部的console.log。(同步優先)2、for循環裡面有一個setTimeout回調,他是墊底的存在,只能最後執行。(回調墊底)那麼,為什麼我們最先輸出的是5呢?
  • C++類與回調函數
    ,它的定義回調函數就是一個通過函數指針調用的函數。如果你把函數的指針(地址)作為參數傳遞給另一個函數,當這個指針被用來調用其所指向的函數時,我們就說這是回調函數。回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用於對該事件或條件進行響應。其實說白了就是把一個函數當做參數傳下去。
  • 這一次,徹底弄懂 JavaScript 執行機制
    js引擎存在monitoring process進程,會持續不斷的檢查主線程執行棧是否為空,一旦為空,就會去Event Queue那裡檢查是否有等待被調用的函數。ajax事件完成,回調函數success進入Event Queue。主線程從Event Queue讀取回調函數success並執行。
  • 如何避免 JavaScript 開發者常犯的 9 個錯誤?
    預期的回調是同步的在 JavaScript 裡,用回調方法處理異步操作。然而,Promises 和 async/await 是處理異步操作的首選方法,因為多次回調會導致回調地獄。回調是不同步的。在延遲執行完成操作之後,它們作為一個函數被調用。
  • javascript 定時器工作原理
    setTimeout()  MDN對 setTimeout 的定義為:在指定的延遲時間之後調用一個函數或執行一個代碼片段。  語法  setTimeout 的語法非常簡單,第一個參數為回調函數,第二個參數為延時的時間。
  • 使用Promise模式來簡化JavaScript的異步回調
    異步請求完成的操作必須預先定義在回調函數中,等到請求完成就必須調用這個函數。這種非線性的異步編程方式會讓開發者很不適應,同時也帶來了諸多的不便,增加了代碼的耦合度和複雜性,代碼的組織上也會很不優雅,大大降低了代碼的可維護性。情況再複雜點,如果一個操作要等到多個異步 ajax 請求的完成才能進行,就會出現回調函數嵌套的情況,如果需要嵌套好幾層,那你就只能自求多福了。
  • [翻譯]JavaScript異步進化史:Callbacks,Promises,Async/Await 上
    當你這樣操作的時候,作為參數傳遞的函數被稱為回調函數,而接受回調函數作為參數的函數稱為高階函數。因為命名很重要,所以這裡重命名代碼裡的變量名,以匹配他們所示範的概念。這種模式看起來應該很熟悉,它無處不在。如果你曾經使用過任何JavaScript中的Array方法,那麼你就已經使用過了回調。
  • javaScript入門—函數和document使用方法,新手必看!
    的alert的對話框,如果沒有檢查一下瀏覽器是否啟用了javascript。JavaScript中函數有什麼特點?函數是重複使用腳本的一種方式,可以重複多次成為可能,在javascript中函數不僅能多次運行而且還可以用來控制什麼時候執行引用腳本。