記錄--沒有await,如何處理“回調地獄”

来源:https://www.cnblogs.com/smileZAZ/archive/2023/12/18/17912003.html
-Advertisement-
Play Games

這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 太長不看 不要嵌套使用函數。給每個函數命名並把他們放在你代碼的頂層 利用函數提升。先使用後聲明。 處理每一個異常 編寫可以復用的函數,並把他們封裝成一個模塊 什麼是“回調地獄”? 非同步Javascript代碼,或者說使用callback的 ...


這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助

太長不看

  • 不要嵌套使用函數。給每個函數命名並把他們放在你代碼的頂層
  • 利用函數提升。先使用後聲明。
  • 處理每一個異常
  • 編寫可以復用的函數,並把他們封裝成一個模塊

什麼是“回調地獄”?

非同步Javascript代碼,或者說使用callback的Javascript代碼,很難符合我們的直觀理解。很多代碼最終會寫成這樣:

fs.readdir(source, function (err, files) {
  if (err) {
    console.log('Error finding files: ' + err)
  } else {
    files.forEach(function (filename, fileIndex) {
      console.log(filename)
      gm(source + filename).size(function (err, values) {
        if (err) {
          console.log('Error identifying file size: ' + err)
        } else {
          console.log(filename + ' : ' + values)
          aspect = (values.width / values.height)
          widths.forEach(function (width, widthIndex) {
            height = Math.round(width / aspect)
            console.log('resizing ' + filename + 'to ' + height + 'x' + height)
            this.resize(width, height).write(dest + 'w' + width + '_' + filename, function(err) {
              if (err) console.log('Error writing file: ' + err)
            })
          }.bind(this))
        }
      })
    })
  }
})

看到上面金字塔形狀的代碼和那些末尾參差不齊的 }) 了嗎?這就是廣為人知的回調地獄了。
人們在編寫JavaScript代碼時,誤認為代碼是按照我們看到的代碼順序從上到下執行的,這就是造成回調地獄的原因。在其他語言中,例如C,Ruby或者Python,第一行代碼執行結束後,才會開始執行第二行代碼,按照這種模式一直到執行到當前文件中最後一行代碼。隨著你學習深入,你會發現JavaScript跟他們是不一樣的。

什麼是回調(callback)?

某種使用JavaScript函數的慣例用法的名字叫做回調。JavaScript語言中沒有一個叫“回調”的東西,它僅僅是一個慣例用法的名字。大多數函數會立刻返回執行結果,使用回調的函數通常會經過一段時間後才輸出結果。名詞“非同步”,簡稱“async”,只是意味著“這將花費一點時間”或者說“在將來某個時間發生而不是現在”。通常回調只使用在I/O操作中,例如下載文件,讀取文件,連接資料庫等等。

當你調用一個正常的函數時,你可以向下麵的代碼那樣使用它的返回值:

var result = multiplyTwoNumbers(5, 10)
console.log(result)
// 50 gets printed out

然而使用回調的非同步函數不會立刻返回任何結果。

var photo = downloadPhoto('http://coolcats.com/cat.gif')
// photo is 'undefined'!

在這種情況下,上面那張gif圖片可能需要很長的時間才能下載完成,但你不想你的程式在等待下載完成的過程中中止(也叫阻塞)。

於是你把需要下載完成後運行的代碼存放到一個函數中(等待下載完成後再運行它)。這就是回調!你把回調傳遞給downloadPhoto函數,當下載結束,回調會被調用。如果下載成功,傳入photo給回調;下載失敗,傳入error給回調。

downloadPhoto('http://coolcats.com/cat.gif', handlePhoto)

function handlePhoto (error, photo) {
  if (error) console.error('Download error!', error)
  else console.log('Download finished', photo)
}

console.log('Download started')

人們理解回調的最大障礙在於理解一個程式的執行順序。在上面的例子中,發生了三件事情。

  1. 聲明handlePhoto函數
  2. downloadPhoto函數被調用並且傳入了handlePhoto最為它的回調
  3. 列印出Download started

請大家註意,起初handlePhoto函數僅僅是被創建並被作為回調傳遞給了downloadPhoto,它還沒有被調用。它會等待downloadPhoto函數完成了它的任務才會執行。這可能需要很長一段時間(取決於網速的快慢)。

這個例子意在闡明兩個重要的概念:

  1. handlePhoto回調只是一個存放將來進行的操作的方式
  2. 事情發生的順序並不是直觀上看到的從上到下,它會當某些事情完成後再跳回來執行。

怎樣解決“回調地獄”問題?

糟糕的編碼習慣造成了回調地獄。幸運的是,編寫優雅的代碼不是那麼難!

你只需要遵循三大原則

1. 減少嵌套層數(Keep your code shallow)

下麵是一堆亂糟糟的代碼,使用browser-request做AJAX請求。

var form = document.querySelector('form')
form.onsubmit = function (submitEvent) {
  var name = document.querySelector('input').value
  request({
    uri: "http://example.com/upload",
    body: name,
    method: "POST"
  }, function (err, response, body) {
    var statusMessage = document.querySelector('.status')
    if (err) return statusMessage.value = err
    statusMessage.value = body
  })
}
這段代碼包含兩個匿名函數,我們來給他們命名。
var form = document.querySelector('form')
form.onsubmit = function formSubmit (submitEvent) {
  var name = document.querySelector('input').value
  request({
    uri: "http://example.com/upload",
    body: name,
    method: "POST"
  }, function postResponse (err, response, body) {
    var statusMessage = document.querySelector('.status')
    if (err) return statusMessage.value = err
    statusMessage.value = body
  })
}

如你所見,給匿名函數一個名字是多麼簡單,而且好處立竿見影:

  • 起一個一望便知其函數功能的名字讓代碼更易讀
  • 當拋出異常時,你可以在stacktrace里看到實際出異常的函數名字,而不是"anonymous"
  • 允許你合理安排函數的位置,並通過函數名字調用它

現在我們可以把這些函數放在我們程式的頂層。

document.querySelector('form').onsubmit = formSubmit

function formSubmit (submitEvent) {
  var name = document.querySelector('input').value
  request({
    uri: "http://example.com/upload",
    body: name,
    method: "POST"
  }, postResponse)
}

function postResponse (err, response, body) {
  var statusMessage = document.querySelector('.status')
  if (err) return statusMessage.value = err
  statusMessage.value = body
}

請大家註意,函數聲明在程式的底部,但是我們在函數聲明之前就可以調用它。這是函數提升的作用。

2.模塊化(Modularize)

任何人都有有能力創建模塊,這點非常重要。寫出一些小模塊,每個模塊只做一件事情,然後把他們組合起來放入其他的模塊做一個複雜的事情。只要你不想陷入回調地獄,你就不會。讓我們把上面的例子修改一下,改為一個模塊。

下麵是一個名為formuploader.js的新文件,包含了我們之前使用過的兩個函數。

module.exports.submit = formSubmit

function formSubmit (submitEvent) {
  var name = document.querySelector('input').value
  request({
    uri: "http://example.com/upload",
    body: name,
    method: "POST"
  }, postResponse)
}

function postResponse (err, response, body) {
  var statusMessage = document.querySelector('.status')
  if (err) return statusMessage.value = err
  statusMessage.value = body
}

module.exports是node.js模塊化的用法。現在已經有了 formuploader.js 文件,我們只需要引入它並使用它。請看下麵的代碼:

var formUploader = require('formuploader')
document.querySelector('form').onsubmit = formUploader.submit

我們的應用只有兩行代碼並且還有以下好處:

  1. 方便新開發人員理解你的代碼 -- 他們不需要費儘力氣讀完formuploader函數的全部代碼
  2. formuploader可以在其他地方復用

3.處理每一個異常(Handle every single error)

有三種不同類型的異常:語法異常,運行時異常和平臺異常。語法異常通常由開發人員在第一次解釋代碼時捕獲,運行時異常通常在代碼運行過程中因為bug觸發,平臺異常通常由於沒有文件的許可權,硬碟錯誤,無網路鏈接等問題造成。這一部分主要來處理最後一種異常:平臺異常。

前兩個大原則意在提高代碼可讀性,但是第三個原則意在提高代碼的穩定性。在你與回調打交道的時候,你通常要處理髮送請求,等待返回或者放棄請求等任務。任何有經驗的開發人員都會告訴你,你從來不知道哪裡回出現問題。所以你有必要提前準備好,異常總是會發生。

把回調函數的第一個參數設置為error對象,是Node.js中處理異常最流行的方式。

var fs = require('fs')

 fs.readFile('/Does/not/exist', handleFile)

 function handleFile (error, file) {
   if (error) return console.error('Uhoh, there was an error', error)
   // otherwise, continue on and use `file` in your code
 }
把第一個參數設為error對象是一個約定俗成的慣例,提醒你記得去處理異常。如果它是第二個參數,你更容易把它忽略掉。

本文轉載於:

https://juejin.cn/post/7294166986195533843

如果對您有所幫助,歡迎您點個關註,我會定時更新技術文檔,大家一起討論學習,一起進步。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 假設你要為用戶名為'username'@'localhost'的用戶設置新密碼,你可以這樣做: 其中,username 是用戶名,localhost 是主機名, 123456是要更改的新密碼。 方法1: 用 ALTER 語句 ALTER USER 'username'@'localhost' IDE ...
  • MySQL提供了多種方法來鎖定解鎖賬號,下麵是幾種常用的方法: 1.使用ALTER語句鎖定賬號 鎖定賬號: ALTER USER 'username'@'localhost' ACCOUNT LOCK; 解鎖賬號: ALTER USER'username'@'localhost' ACCOUNT U ...
  • 本文分享自華為雲社區《你的JoinHint為什麼不生效【綻放吧!GaussDB(DWS)雲原生數倉】》,作者:你是猴子請來的救兵嗎 。 引言 提起資料庫的Hint,幾乎每個DBA都知道這一強大功能。在GaussDB(DWS)中,Hint可以被用來干預SQL的執行計劃,但是在日常工作中,很多開發人員對 ...
  • 夜黑風高的某一晚,突然收到一條運營後臺資料庫慢sql的報警,耗時竟然達到了60s。看了一下,還好不是很頻繁,內心會更加從容排查問題,應該是特定條件下沒有走到索引導致,如果頻繁出現慢查詢,可能會將資料庫連接池打滿,導致資料庫不可用,從而導致應用不可用。 ...
  • 最近開始體驗FastGPT開源知識庫問答系統,用他們試著開發調試一些小助手。這中間需要使用到MongoDB,就在自己伺服器上進行了安裝,特此記錄下。 環境說明:阿裡雲ECS,2核8G,X86架構,CentOS 7.9操作系統。 選擇版本 1.打開MongoDB社區版下載頁面,選擇我們想要安裝的版本、 ...
  • 直接加減數字 select sysdate 當前時間, sysdate + 1 加一天, sysdate - 1 減一天, sysdate + (1 / 24) 加一小時, sysdate + (1 / 24 / 60) 加一分鐘 from dual; 使用add_months()函數 select ...
  • 一、垂直分庫場景 場景:在業務系統中,涉及一下表結構,但是由於用戶與訂單每天都會產生大量的數據,單台伺服器的數據存儲以及處理能力是有限的,可以對資料庫表進行拆分,原有資料庫如下 說明1:整個業務系統中的表,大致分為四個,商品信息類的表,訂單相關的表,用戶相關表及省市區相關的表,這裡暫時將省市區的表和 ...
  • 成功之前我們要做應該做的事情,成功之後我們才可以做喜歡做的事情。 1. 處理器架構 CPU 架構是 CPU 廠商給屬於同一系列的 CPU 產品定的一個規範,主要目的是為了區分不同類型 CPU 的重要標示。市面上的 CPU 分類主要分有兩大陣營,一個是 intel、AMD 為首的 複雜指令集 CPU, ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...