摘要:什么是回調地獄異步代碼,或者說使用的代碼,很難符合我們的直觀理解。人們理解回調的最大障礙在于理解一個程序的執行順序。怎樣解決回調地獄問題糟糕的編碼習慣造成了回調地獄。把回調函數的第一個參數設置為對象,是中處理異常最流行的方式。
什么是“回調地獄”?
異步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跟他們是不一樣的。
某種使用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")
人們理解回調的最大障礙在于理解一個程序的執行順序。在上面的例子中,發生了三件事情。
聲明handlePhoto函數
downloadPhoto函數被調用并且傳入了handlePhoto最為它的回調
打印出Download started。
請大家注意,起初handlePhoto函數僅僅是被創建并被作為回調傳遞給了downloadPhoto,它還沒有被調用。它會等待downloadPhoto函數完成了它的任務才會執行。這可能需要很長一段時間(取決于網速的快慢)。
這個例子意在闡明兩個重要的概念:
handlePhoto回調只是一個存放將來進行的操作的方式
事情發生的順序并不是直觀上看到的從上到下,它會當某些事情完成后再跳回來執行。
怎樣解決“回調地獄”問題?糟糕的編碼習慣造成了回調地獄。幸運的是,編寫優雅的代碼不是那么難!
你只需要遵循三大原則:
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)任何人都有有能力創建模塊,這點非常重要。Isaac Schlueter(NodeJS項目成員)說過“寫出一些小模塊,每個模塊只做一件事情,然后把他們組合起來放入其他的模塊做一個復雜的事情。只要你不想陷入回調地獄,你就不會落入那般田地。”
。
讓我們把上面的例子修改一下,改為一個模塊。
下面是一個名為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
我們的應用只有兩行代碼并且還有以下好處:
方便新開發人員理解你的代碼 -- 他們不需要費盡力氣讀完formuploader函數的全部代碼
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對象是一個約定俗成的慣例,提醒你記得去處理異常。如果它是第二個參數,你更容易把它忽略掉。
總結不要嵌套使用函數。給每個函數命名并把他們放在你代碼的頂層
利用函數提升。先使用后聲明。
處理每一個異常
編寫可以復用的函數,并把他們封裝成一個模塊
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/99949.html
摘要:什么是在中什么時候需要是中的包管理器。允許我們為安裝各種模塊,這個包管理器為我們提供了安裝刪除等其它命令來管理模塊。 showImg(https://user-gold-cdn.xitu.io/2019/7/11/16bde5b2df52a924?w=4000&h=2667&f=jpeg&s=450648); 本文為您分享「Node.js 入門你需要知道的 10 個問題」這些問題可能也...
摘要:回調地獄異步程序書寫指南什么是回調地獄我們很難一眼就看懂異步,或者是使用回調函數的程序。通常回調函數會用在下載文件讀取文件或者數據庫相關事務等。注意還沒有被調用,它只是被創建然后最為回調函數傳入。 回調地獄 JavaScript異步程序書寫指南 什么是回調地獄? 我們很難一眼就看懂異步JavaScript,或者是使用回調函數的JavaScript程序。例如下面這段代碼: fs.read...
摘要:最受歡迎的引擎是,在和中使用,用于,以及所使用的。單線程的我們說是單線程的,因為有一個調用棧處理我們的函數。也就是說,如果有其他函數等待執行,函數是不能離開調用棧的。每個異步函數在被送入調用棧之前必須通過回調隊列。 翻譯:瘋狂的技術宅原文:https://www.valentinog.com/bl... 本文首發微信公眾號:前端先鋒歡迎關注,每天都給你推送新鮮的前端技術文章 sh...
摘要:異步問題回調地獄首先,我們來看下異步編程中最常見的一種問題,便是回調地獄。同時使用也是異步編程最基礎和核心的一種解決思路。基于,目前也被廣泛運用,其是異步編程的一種解決方案,比傳統的回調函數解決方案更合理和強大。 關于 微信公眾號:前端呼啦圈(Love-FED) 我的博客:勞卜的博客 知乎專欄:前端呼啦圈 前言 在實際編碼中,我們經常會遇到Javascript代碼異步執行的場景...
摘要:編寫異步代碼可能是一種不同的體驗,尤其是對異步控制流而言。回調函數的準則在編寫異步代碼時,要記住的第一個規則是在定義回調時不要濫用閉包。為回調創建命名函數,避免使用閉包,并將中間結果作為參數傳遞。 本系列文章為《Node.js Design Patterns Second Edition》的原文翻譯和讀書筆記,在GitHub連載更新,同步翻譯版鏈接。 歡迎關注我的專欄,之后的博文將在專...
閱讀 3688·2021-11-19 09:56
閱讀 1468·2021-09-22 15:11
閱讀 1127·2019-08-30 15:55
閱讀 3371·2019-08-29 14:02
閱讀 2911·2019-08-29 11:07
閱讀 433·2019-08-28 17:52
閱讀 3172·2019-08-26 13:59
閱讀 436·2019-08-26 13:53