摘要:理解回調(diào)和原文自工程師博客,傳送門這兩個概念是編程語言的基本內(nèi)容?;卣{(diào)地獄就是濫用回調(diào)。通常,在回調(diào)中,錯誤作為第一個參數(shù)傳遞。這個具有這兩個函數(shù)作為參數(shù)的回調(diào)稱為執(zhí)行程序。到目前為止,我希望我已經(jīng)讓自己了解了回調(diào)和。
理解回調(diào)和Promise
原文自工程師Fernando Hernandez博客,傳送門
這兩個概念是Javascript編程語言的基本內(nèi)容。因為這種語言是在異步編程的范例下工作。
所以,我決定分享這篇文章,以便了解這兩個用來執(zhí)行異步操作的特性——回調(diào)和Promise是什么。
那么,我們開始吧!
回調(diào)為了理解回調(diào),我將做一個簡短的比喻。
假設(shè)我們正在通電話。在談話時,出現(xiàn)了需要立即解決的情況。我們把電話掛了,我們先做需要立即解決的事情,當(dāng)我們完成時,我們再回到我們剛剛暫停的電話。
好吧,通過這個例子,我們可以大致了解什么是回調(diào)。
現(xiàn)在,用編程語言說。
回調(diào)是在異步操作已經(jīng)完成后將要執(zhí)行的功能。
回調(diào)作為參數(shù)傳遞給異步操作。通常,是作為函數(shù)的最后一個參數(shù)傳遞的。這樣做是一種很好的做法,所以請記住這一點。
回調(diào)的結(jié)構(gòu)如下所示:
function sayHello() { console.log("Hello everyone"); } setTimeout(()=>{sayHello()}, 3000);
我們在上面的例子中所做的是,首先定義一個向控制臺輸出消息的函數(shù)。之后,我們使用一個名為setTimeout的計時器(此計時器是一個本機Javascript函數(shù))。此計時器是一個異步操作,在一定時間后執(zhí)行回調(diào)。在這個例子中,在3000ms(3秒)之后將執(zhí)行sayHello函數(shù)。
回調(diào)模式正如我們在開始時提到的那樣,作為優(yōu)秀的開發(fā)人員,我們應(yīng)該將回調(diào)位置視為參數(shù)。應(yīng)始終將其作為最后一個。這就是回調(diào)模式的名稱。
通過這種方式,我們的代碼將更具可讀性,并且當(dāng)其他程序員處理它時將更容易維護。
我們來看另一個回調(diào)示例:
const fs = require("fs") // Importing Nodejs library // Declaring file path const filePath = "./users.json" // Asynchronous operation to read the file fs.readFile(filePath, function onReadFile(err, result) { // In case of error print it in the console if (err) { console.log("There was an error: " + err) return // Get out of the function } // Print on the console the file and the content of it. console.log("The file was successfully read it: " + result) })
在這里,我們使用Nodejs庫,用于在我們的文件系統(tǒng)上進行操作。在該示例中,我們使用readFile函數(shù)來從我們的計算機中讀取文件。此函數(shù)接收兩個參數(shù)(文件路徑和回調(diào))。我們可以注意到,名為onReadFile的回調(diào)它是最后一個參數(shù)。
匿名聲明回調(diào)是很常見的,但是如果會出現(xiàn)錯誤的情況,最好為它指定一個名稱,以便更容易地識別它。
最后,直到我們的代碼完成讀取所請求的文件將會執(zhí)行該回調(diào)。如果存在,Javascript將在此過程中繼續(xù)執(zhí)行代碼。
回調(diào)地獄一旦你知道回調(diào)函數(shù)是如何工作的,并付諸實踐,我們就必須記住一些東西。作為一名優(yōu)秀的開發(fā)人員,我們必須知道如何使用它,并避免像回調(diào)地獄這樣糟糕的事情。
回調(diào)地獄就是濫用回調(diào)。 它看起來像這樣:
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)) } }) }) } })
基本上,我們可以看到,使用嵌套回調(diào)是一種不好的做法,它會在視覺上產(chǎn)生一種金字塔式的效果。這將成為難以維護和讀取的代碼,我們不希望這樣。
如何避免回調(diào)地獄?命名函數(shù):正如我之前所說,你可以做的第一件事是命名你的函數(shù)(回調(diào))。因此,當(dāng)發(fā)生錯誤時,它將使用函數(shù)名稱以特定方式指示錯誤。此外,這會使你的代碼更具可讀性,當(dāng)其他程序員閱讀時,它們更容易維護它。
模塊化:一旦命名了函數(shù),就可以開始多帶帶定義它們。這樣,您將只用輸入回調(diào)名稱。首先,可以在同一文件底部定義它們。除此之外,另一種方法是將該函數(shù)寫入多帶帶的文件。這樣,我們可以在任何文件中導(dǎo)出和導(dǎo)入它。
這使得我們代碼更具有可重用性,有更高的可讀性和易維護性。
處理錯誤:編寫代碼時,我們必須記住錯誤總是會發(fā)生。為了能夠輕松地識別定位它們,編寫處理可能發(fā)生的錯誤的代碼非常重要。
通常,在回調(diào)中,錯誤作為第一個參數(shù)傳遞。我們可以通過以下方式處理錯誤:
const fs = require("fs") const filePath = "./users.json" fs.readFile(filePath, handleFile) function handleFile(err, result) { if (err) { return console.log("There was an error: " + err) } console.log("File: " + result) }
養(yǎng)成良好的編程習(xí)慣,讓其余程序員不會恨你一輩子!
PromiseJavascript中的Promise就是相當(dāng)于字面意思上的承諾。我們知道,當(dāng)我們做出承諾時,這意味著我們將盡一切可能實現(xiàn)預(yù)期的結(jié)果。但是,我們也知道,由于某種原因,不能總是履行承諾。
正如承諾在現(xiàn)實生活中一樣,它是在Javascript中,則另一種方式表示即代碼。
讓我們看一個Promise的例子:
let promise = new Promise(function(resolve, reject) { // things to do to accomplish your promise if(/* everything turned out fine */) { resolve("Stuff worked") } else { // for some reason the promise doesn"t fulfilled reject(new Error("it broke")) } })
Promise是Javascript的原生類(自ES6起)。
promise的構(gòu)造函數(shù)接收一個參數(shù):一個回調(diào),它有兩個參數(shù):
resolve
reject
這些是已經(jīng)在Javascript中定義的函數(shù),因此我們不用自己去構(gòu)建它們。
這個具有這兩個函數(shù)作為參數(shù)的回調(diào)稱為執(zhí)行程序。
執(zhí)行者在創(chuàng)建承諾時立即運行。
執(zhí)行函數(shù)將執(zhí)行什么?好吧,在這里面,我們將放置所有必要的代碼來實現(xiàn)我們的承諾。
一旦執(zhí)行程序完成執(zhí)行,我們將發(fā)送其中一個函數(shù)作為參數(shù)。
如果實現(xiàn)了,我們使用resolve函數(shù)。
如果由于某種原因失敗,我們使用reject函數(shù)。
函數(shù)resolve和reject,只接收一個參數(shù)。reject函數(shù)通常會使用Error類傳遞錯誤,正如我們在前面的示例中所看到的那樣。
Promise有三個獨特的狀態(tài):
Pending:異步操作尚未完成。
Fulfilled:異步操作已完成并返回一個值。
Rejected:指示異步操作失敗以及失敗的原因。
Promise對象有兩個屬性:
State:表示Promise的狀態(tài)。
Result:存儲Promise的值(如果已滿足)或錯誤(如果已拒絕)。
最初,Promise的狀態(tài)為“pending”,結(jié)果為“undefined”。
一旦promise完成執(zhí)行,promise的狀態(tài)和結(jié)果將被修改為相應(yīng)的值。取決于promise是否已完成或被拒絕。
讓我們看看下面的圖表來更好地理解它:
一旦promise改變了他們的狀態(tài),他們就無法逆轉(zhuǎn)。
如何使用或調(diào)用Promise?為了使用我們創(chuàng)建的Promise,我們使用then和catch函數(shù)。在代碼中,它們看起來像這樣:
promise.then(function(result) { console.log(result) }).catch(function(err) { console.log(err) })
then允許我們處理已完成或已執(zhí)行狀態(tài)的promise
函數(shù)catch將允許我們處理被拒絕狀態(tài)的promise
在then函數(shù)中,我們也可以處理被拒絕的promise。為此,處理程序接收兩個參數(shù)。第一個是已完成的promise,第二個是被拒絕的promise。通過這種方式:
promise.then(function(result) { // Handling the value console.log(result) }, function(err) { // Handling the error console.log(err) })
處理程序then和catch都是異步的。
基本上,一旦Javascript執(zhí)行了下面的代碼,就會執(zhí)行then和catch。
例:
promise.then(function(result) { console.log(result) }).catch(function(err) { console.log(err) }) console.log("Hello world")
我們可能認為首先它會先在控制臺輸出在promise中的value或error。但是要知道它們是異步操作,我們必須記住它將花費最少的時間來執(zhí)行,因此消息“Hello world”還是會首先顯示。
Promise類有一個名為all的方法,用于執(zhí)行promise數(shù)組。它看起來像這樣:
Promise.all([ new Promise.((resolve, reject) => setTimeout(() => resolve(1), 3000)), // 1 new Promise.((resolve, reject) => setTimeout(() => resolve(2), 2000)), // 2 new Promise.((resolve, reject) => setTimeout(() => resolve(3), 1000)), // 3 ]).then(result => console.log(result)) // 1, 2, 3
在隨后處理程序?qū)⒃诳刂婆_輸出每個promise的結(jié)果的數(shù)組。
如果其中一個promise被reject,則該函數(shù)將被reject并出現(xiàn)錯誤。如下所示:
Promise.all([ new Promise.((resolve, reject) => setTimeout(() => resolve(1), 3000)), // 1 new Promise.((resolve, reject) => setTimeout(() => resolve(2), 2000)), // 2 new Promise.((resolve, reject) => setTimeout(() => reject(new Error("An error has ocurred")), 1000)) ]).then(result => console.log(result)) .catch(err => console.log(err)) // An error has ocurred
還有另一種類似于all的方法,但又有所不同。它是race方法。
與all函數(shù)相同,它接收一個promise數(shù)組,但它將返回先完成或拒絕的promise。我們來看一個代碼示例:
let promise1 = new Promise(function(resolve, reject) { setTimeout(function() { resolve("promise one") }, 3000) // Resolve after 3 seconds }) let promise2 = new Promise(function(resolve, reject) { setTimeout(function() { resolve("promise two") }, 1000) // Resolve after 1 seconds }) Promise.race([ promise1, promise2 ]).then(result => console.log(result)) // promise two
我們可以看到,返回給我們的值是第二個promise返回的。這是因為第一個promise是先執(zhí)行的。
讓我們看一個被拒絕的promise的另一個例子:
let promise1 = new Promise(function(resolve, reject) { setTimeout(function() { resolve("promise one") }, 3000) // Resolve after 3 seconds }) let promise2 = new Promise(function(resolve, reject) { setTimeout(function() { resolve("promise two") }, 2000) // Resolve after 2 seconds }) let promise3 = new Promise(function(resolve, reject) { setTimeout(function() { reject("promise three rejected") }, 1000) // Reject after 1 second }) Promise.race([ promise1, promise2, promise3 ]).then(result => console.log(result)) .catch(err => console.log(err)) // promise three is rejected
在這段代碼race函數(shù)中,將要打印的是在我們聲明的第三個promise中發(fā)現(xiàn)的錯誤。你可以想象得到為什么。實際上,第三個promise比其他promise先執(zhí)行。
因此,無論promise是否被拒絕或完成,race方法將執(zhí)行第一個并忽略其他方法。
到目前為止,我希望我已經(jīng)讓自己了解了回調(diào)和promise?;旧?,Javascript的這兩個特性用于處理異步操作。這就是這門語言的基礎(chǔ),因此它很受歡迎。
我將很快繼續(xù)關(guān)于處理異步的另一篇文章——Async-Await。
譯者總結(jié)本文是小編的第一次譯文,翻譯不到位請見諒。由于突然想重溫一下Promise,為此對Promise的知識點進行了再次溫故,看看從不同人的角度怎么去理解Promise的。上文對Promise進行了簡單的介紹并附帶一些回調(diào)的知識點,也讓我對回調(diào)有了新的見解。相信對讀者也會有所幫助,我會再接再厲的!
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/101823.html
摘要:直到最近,我們?nèi)匀辉谟煤唵蔚幕卣{(diào)函數(shù)來處理異步的問題。當(dāng)我們只有一個異步任務(wù)的時候使用回調(diào)函數(shù)看起來還不會有什么問題。 原文地址:http://blog.getify.com/promis... 廈門旅行歸來,繼續(xù)理解Promise 在上一篇深入理解Promise五部曲:1.異步問題中,我們揭示了JS的異步事件輪詢并發(fā)模型并且解釋了多任務(wù)是如何相互穿插使得它們看起來像是同時運行的。...
摘要:當(dāng)引擎開始執(zhí)行一個函數(shù)比如回調(diào)函數(shù)時,它就會把這個函數(shù)執(zhí)行完,也就是說只有執(zhí)行完這段代碼才會繼續(xù)執(zhí)行后面的代碼。當(dāng)條件允許時,回調(diào)函數(shù)就會被運行?,F(xiàn)在,返回去執(zhí)行注冊的那個回調(diào)函數(shù)。 原文地址:http://blog.getify.com/promis... 在微博上看到有人分享LabJS作者寫的關(guān)于Promise的博客,看了下覺得寫得很好,分五個部分講解了Promise的來龍去脈。從...
摘要:回調(diào)方式將回調(diào)函數(shù)作為參數(shù)傳遞給主函數(shù),同時在主函數(shù)內(nèi)部處理錯誤信息。模塊是促進中對象之間交流的模塊,它是異步事件驅(qū)動機制的核心。在異步函數(shù)的回調(diào)中,根據(jù)執(zhí)行情況觸發(fā)或者事件。比如,當(dāng)異常事件觸發(fā)關(guān)閉數(shù)據(jù)庫的動作時。 原文鏈接:Understanding Nodejs Event-driven Architecture 作者:Samer Buna 翻譯:野草 本文首發(fā)于前端早讀課【...
摘要:事件驅(qū)動機制的最簡單形式,是在中十分流行的回調(diào)函數(shù),例如。在回調(diào)函數(shù)這種形式中,事件每被觸發(fā)一次,回調(diào)就會被觸發(fā)一次?;卣{(diào)函數(shù)需要作為宿主函數(shù)的一個參數(shù)進行傳遞多個宿主回調(diào)進行嵌套就形成了回調(diào)地獄,而且錯誤和成功都只能在其中進行處理。 學(xué)習(xí) Node.js 一定要理解的內(nèi)容之一,文中主要涉及到了 EventEmitter 的使用和一些異步情況的處理,比較偏基礎(chǔ),值得一讀。 閱讀原文 大...
摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。寫一個符合規(guī)范并可配合使用的寫一個符合規(guī)范并可配合使用的理解的工作原理采用回調(diào)函數(shù)來處理異步編程。 JavaScript怎么使用循環(huán)代替(異步)遞歸 問題描述 在開發(fā)過程中,遇到一個需求:在系統(tǒng)初始化時通過http獲取一個第三方服務(wù)器端的列表,第三方服務(wù)器提供了一個接口,可通過...
閱讀 816·2021-11-12 10:36
閱讀 3409·2021-09-08 10:44
閱讀 2767·2019-08-30 11:08
閱讀 1429·2019-08-29 16:12
閱讀 2695·2019-08-29 12:24
閱讀 921·2019-08-26 10:14
閱讀 709·2019-08-23 18:32
閱讀 1202·2019-08-23 17:52