成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

回調(diào)地獄-編寫異步JavaScript指南

劉玉平 / 549人閱讀

摘要:什么是回調(diào)地獄異步代碼,或者說使用的代碼,很難符合我們的直觀理解。人們理解回調(diào)的最大障礙在于理解一個(gè)程序的執(zhí)行順序。怎樣解決回調(diào)地獄問題糟糕的編碼習(xí)慣造成了回調(diào)地獄。把回調(diào)函數(shù)的第一個(gè)參數(shù)設(shè)置為對(duì)象,是中處理異常最流行的方式。

什么是“回調(diào)地獄”?

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

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)地獄了。
人們?cè)诰帉慗avaScript代碼時(shí),誤認(rèn)為代碼是按照我們看到的代碼順序從上到下執(zhí)行的,這就是造成回調(diào)地獄的原因。在其他語言中,例如C,Ruby或者Python,第一行代碼執(zhí)行結(jié)束后,才會(huì)開始執(zhí)行第二行代碼,按照這種模式一直到執(zhí)行到當(dāng)前文件中最后一行代碼。隨著你學(xué)習(xí)深入,你會(huì)發(fā)現(xiàn)JavaScript跟他們是不一樣的。

什么是回調(diào)(callback)?

某種使用JavaScript函數(shù)的慣例用法的名字叫做回調(diào)。JavaScript語言中沒有一個(gè)叫“回調(diào)”的東西,它僅僅是一個(gè)慣例用法的名字。大多數(shù)函數(shù)會(huì)立刻返回執(zhí)行結(jié)果,使用回調(diào)的函數(shù)通常會(huì)經(jīng)過一段時(shí)間后才輸出結(jié)果。名詞“異步”,簡(jiǎn)稱“async”,只是意味著“這將花費(fèi)一點(diǎn)時(shí)間”或者說“在將來某個(gè)時(shí)間發(fā)生而不是現(xiàn)在”。通常回調(diào)只使用在I/O操作中,例如下載文件,讀取文件,連接數(shù)據(jù)庫(kù)等等。

當(dāng)你調(diào)用一個(gè)正常的函數(shù)時(shí),你可以向下面的代碼那樣使用它的返回值:

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

然而使用回調(diào)的異步函數(shù)不會(huì)立刻返回任何結(jié)果。

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

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

于是你把需要下載完成后運(yùn)行的代碼存放到一個(gè)函數(shù)中(等待下載完成后再運(yùn)行它)。這就是回調(diào)!你把回調(diào)傳遞給downloadPhoto函數(shù),當(dāng)下載結(jié)束,回調(diào)會(huì)被調(diào)用。如果下載成功,傳入photo給回調(diào);下載失敗,傳入error給回調(diào)。

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")

人們理解回調(diào)的最大障礙在于理解一個(gè)程序的執(zhí)行順序。在上面的例子中,發(fā)生了三件事情。

聲明handlePhoto函數(shù)

downloadPhoto函數(shù)被調(diào)用并且傳入了handlePhoto最為它的回調(diào)

打印出Download started。

請(qǐng)大家注意,起初handlePhoto函數(shù)僅僅是被創(chuàng)建并被作為回調(diào)傳遞給了downloadPhoto,它還沒有被調(diào)用。它會(huì)等待downloadPhoto函數(shù)完成了它的任務(wù)才會(huì)執(zhí)行。這可能需要很長(zhǎng)一段時(shí)間(取決于網(wǎng)速的快慢)。

這個(gè)例子意在闡明兩個(gè)重要的概念:

handlePhoto回調(diào)只是一個(gè)存放將來進(jìn)行的操作的方式

事情發(fā)生的順序并不是直觀上看到的從上到下,它會(huì)當(dāng)某些事情完成后再跳回來執(zhí)行。

怎樣解決“回調(diào)地獄”問題?

糟糕的編碼習(xí)慣造成了回調(diào)地獄。幸運(yùn)的是,編寫優(yōu)雅的代碼不是那么難!

你只需要遵循三大原則

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

下面是一堆亂糟糟的代碼,使用browser-request做AJAX請(qǐng)求。

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
  })
}

這段代碼包含兩個(gè)匿名函數(shù),我們來給他們命名。

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
  })
}

如你所見,給匿名函數(shù)一個(gè)名字是多么簡(jiǎn)單,而且好處立竿見影:

起一個(gè)一望便知其函數(shù)功能的名字讓代碼更易讀

當(dāng)拋出異常時(shí),你可以在stacktrace里看到實(shí)際出異常的函數(shù)名字,而不是"anonymous"

允許你合理安排函數(shù)的位置,并通過函數(shù)名字調(diào)用它

現(xiàn)在我們可以把這些函數(shù)放在我們程序的頂層。

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
}

請(qǐng)大家注意,函數(shù)聲明在程序的底部,但是我們?cè)诤瘮?shù)聲明之前就可以調(diào)用它。這是函數(shù)提升的作用。

2.模塊化(Modularize)

任何人都有有能力創(chuàng)建模塊,這點(diǎn)非常重要。Isaac Schlueter(NodeJS項(xiàng)目成員)說過“寫出一些小模塊,每個(gè)模塊只做一件事情,然后把他們組合起來放入其他的模塊做一個(gè)復(fù)雜的事情。只要你不想陷入回調(diào)地獄,你就不會(huì)落入那般田地?!?br>。
讓我們把上面的例子修改一下,改為一個(gè)模塊。

下面是一個(gè)名為formuploader.js的新文件,包含了我們之前使用過的兩個(gè)函數(shù)。

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模塊化的用法。現(xiàn)在已經(jīng)有了 formuploader.js 文件,我們只需要引入它并使用它。請(qǐng)看下面的代碼:

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

我們的應(yīng)用只有兩行代碼并且還有以下好處:

方便新開發(fā)人員理解你的代碼 -- 他們不需要費(fèi)盡力氣讀完formuploader函數(shù)的全部代碼

formuploader可以在其他地方復(fù)用

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

有三種不同類型的異常:語法異常,運(yùn)行時(shí)異常和平臺(tái)異常。語法異常通常由開發(fā)人員在第一次解釋代碼時(shí)捕獲,運(yùn)行時(shí)異常通常在代碼運(yùn)行過程中因?yàn)閎ug觸發(fā),平臺(tái)異常通常由于沒有文件的權(quán)限,硬盤錯(cuò)誤,無網(wǎng)絡(luò)鏈接等問題造成。這一部分主要來處理最后一種異常:平臺(tái)異常。

前兩個(gè)大原則意在提高代碼可讀性,但是第三個(gè)原則意在提高代碼的穩(wěn)定性。在你與回調(diào)打交道的時(shí)候,你通常要處理發(fā)送請(qǐng)求,等待返回或者放棄請(qǐng)求等任務(wù)。任何有經(jīng)驗(yàn)的開發(fā)人員都會(huì)告訴你,你從來不知道哪里回出現(xiàn)問題。所以你有必要提前準(zhǔn)備好,異??偸菚?huì)發(fā)生。

把回調(diào)函數(shù)的第一個(gè)參數(shù)設(shè)置為error對(duì)象,是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
 }

把第一個(gè)參數(shù)設(shè)為error對(duì)象是一個(gè)約定俗成的慣例,提醒你記得去處理異常。如果它是第二個(gè)參數(shù),你更容易把它忽略掉。

總結(jié)

不要嵌套使用函數(shù)。給每個(gè)函數(shù)命名并把他們放在你代碼的頂層

利用函數(shù)提升。先使用后聲明。

處理每一個(gè)異常

編寫可以復(fù)用的函數(shù),并把他們封裝成一個(gè)模塊

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/99949.html

相關(guān)文章

  • Node.js 入門你需要知道的 10 個(gè)問題

    摘要:什么是在中什么時(shí)候需要是中的包管理器。允許我們?yōu)榘惭b各種模塊,這個(gè)包管理器為我們提供了安裝刪除等其它命令來管理模塊。 showImg(https://user-gold-cdn.xitu.io/2019/7/11/16bde5b2df52a924?w=4000&h=2667&f=jpeg&s=450648); 本文為您分享「Node.js 入門你需要知道的 10 個(gè)問題」這些問題可能也...

    szysky 評(píng)論0 收藏0
  • 【翻譯】關(guān)于回調(diào)地獄

    摘要:回調(diào)地獄異步程序書寫指南什么是回調(diào)地獄我們很難一眼就看懂異步,或者是使用回調(diào)函數(shù)的程序。通常回調(diào)函數(shù)會(huì)用在下載文件讀取文件或者數(shù)據(jù)庫(kù)相關(guān)事務(wù)等。注意還沒有被調(diào)用,它只是被創(chuàng)建然后最為回調(diào)函數(shù)傳入。 回調(diào)地獄 JavaScript異步程序書寫指南 什么是回調(diào)地獄? 我們很難一眼就看懂異步JavaScript,或者是使用回調(diào)函數(shù)的JavaScript程序。例如下面這段代碼: fs.read...

    Betta 評(píng)論0 收藏0
  • JavaScript引擎是如何工作的?從調(diào)用棧到Promise你需要知道的一切

    摘要:最受歡迎的引擎是,在和中使用,用于,以及所使用的。單線程的我們說是單線程的,因?yàn)橛幸粋€(gè)調(diào)用棧處理我們的函數(shù)。也就是說,如果有其他函數(shù)等待執(zhí)行,函數(shù)是不能離開調(diào)用棧的。每個(gè)異步函數(shù)在被送入調(diào)用棧之前必須通過回調(diào)隊(duì)列。 翻譯:瘋狂的技術(shù)宅原文:https://www.valentinog.com/bl... 本文首發(fā)微信公眾號(hào):前端先鋒歡迎關(guān)注,每天都給你推送新鮮的前端技術(shù)文章 sh...

    Simon_Zhou 評(píng)論0 收藏0
  • 談?wù)?em>JavaScript異步代碼優(yōu)化

    摘要:異步問題回調(diào)地獄首先,我們來看下異步編程中最常見的一種問題,便是回調(diào)地獄。同時(shí)使用也是異步編程最基礎(chǔ)和核心的一種解決思路?;冢壳耙脖粡V泛運(yùn)用,其是異步編程的一種解決方案,比傳統(tǒng)的回調(diào)函數(shù)解決方案更合理和強(qiáng)大。 關(guān)于 微信公眾號(hào):前端呼啦圈(Love-FED) 我的博客:勞卜的博客 知乎專欄:前端呼啦圈 前言 在實(shí)際編碼中,我們經(jīng)常會(huì)遇到Javascript代碼異步執(zhí)行的場(chǎng)景...

    chnmagnus 評(píng)論0 收藏0
  • 《Node.js設(shè)計(jì)模式》基于回調(diào)異步控制流

    摘要:編寫異步代碼可能是一種不同的體驗(yàn),尤其是對(duì)異步控制流而言?;卣{(diào)函數(shù)的準(zhǔn)則在編寫異步代碼時(shí),要記住的第一個(gè)規(guī)則是在定義回調(diào)時(shí)不要濫用閉包。為回調(diào)創(chuàng)建命名函數(shù),避免使用閉包,并將中間結(jié)果作為參數(shù)傳遞。 本系列文章為《Node.js Design Patterns Second Edition》的原文翻譯和讀書筆記,在GitHub連載更新,同步翻譯版鏈接。 歡迎關(guān)注我的專欄,之后的博文將在專...

    Chiclaim 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<