亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

展示JavaScript中異步與回調(diào)的基本概念及回調(diào)地獄

3403771864 / 456人閱讀

  JavaScript異步與回調(diào)

  一、前言

  首先我們要記住的是異步和并行有著本質(zhì)的區(qū)別。

  并行,簡(jiǎn)單來(lái)說(shuō)是一般指并行計(jì)算,就是說(shuō)同一時(shí)刻有多條指令同時(shí)被執(zhí)行,這些指令可能執(zhí)行于同一CPU的多核上,或者多個(gè)CPU上,或者多個(gè)物理主機(jī)甚至多個(gè)網(wǎng)絡(luò)中。

  同步,一般指按照預(yù)定的順序依次執(zhí)行任務(wù),只有當(dāng)上一個(gè)任務(wù)完成后,才開(kāi)始執(zhí)行下一個(gè)任務(wù)。

  異步,與同步相對(duì)應(yīng),異步指的是讓CPU暫時(shí)擱置當(dāng)前任務(wù),先處理下一個(gè)任務(wù),當(dāng)收到上個(gè)任務(wù)的回調(diào)通知后,再返回上個(gè)任務(wù)繼續(xù)執(zhí)行,整個(gè)過(guò)程無(wú)需第二個(gè)線程參與。

  文字表述很讓人很費(fèi)解,現(xiàn)在就用圖片形式來(lái)表達(dá)并行、同步和異步更為直觀,假設(shè)現(xiàn)在有A、B兩個(gè)任務(wù)需要處理,使用并行、同步和異步的處理方式會(huì)分別采用如下圖所示的執(zhí)行方式:

  二、異步函數(shù)

  JavaScript為我們提供了許多異步的函數(shù),這些函數(shù)允許我們方便的執(zhí)行異步任務(wù),也就是說(shuō),我們現(xiàn)在開(kāi)始執(zhí)行一個(gè)任務(wù)(函數(shù)),但任務(wù)會(huì)在稍后完成,具體完成時(shí)間并不清楚。

  例如,setTimeout函數(shù)就是一個(gè)非常典型的異步函數(shù),此外,fs.readFile、fs.writeFile同樣也是異步函數(shù)。

  我們可以自己定義一個(gè)異步任務(wù)的案例,例如自定義一個(gè)文件復(fù)制函數(shù)copyFile(from,to):

  const fs = require('fs')
  function copyFile(from, to) {
  fs.readFile(from, (err, data) => {
  if (err) {
  console.log(err.message)
  return
  }
  fs.writeFile(to, data, (err) => {
  if (err) {
  console.log(err.message)
  return
  }
  console.log('Copy finished')
  })
  })
  }

  函數(shù)copyFile首先從參數(shù)from讀取文件數(shù)據(jù),隨后將數(shù)據(jù)寫入?yún)?shù)to指向的文件。

  下面展示的就是調(diào)用copyFile:

  copyFile('./from.txt','./to.txt')//復(fù)制文件

  現(xiàn)在要注意這個(gè)節(jié)點(diǎn),copyFile(...)后面還有其他代碼,那么程序不會(huì)等待copyFile執(zhí)行結(jié)束,而是直接向下執(zhí)行,文件復(fù)制任務(wù)何時(shí)結(jié)束,程序并不關(guān)心。

  copyFile('./from.txt','./to.txt')
  //下面的代碼不會(huì)等待上面的代碼執(zhí)行結(jié)束
  ...

  到目前為止是正常的,可后面,如果我們?cè)赾opyFile(...)函數(shù)后,直接訪問(wèn)文件./to.txt中的內(nèi)容會(huì)發(fā)生什么呢?

  這將不會(huì)讀到復(fù)制過(guò)來(lái)的內(nèi)容,就行這樣:

  copyFile('./from.txt','./to.txt')
  fs.readFile('./to.txt',(err,data)=>{
  ...
  })

  如果在執(zhí)行程序之前,./to.txt文件還沒(méi)有創(chuàng)建,將得到如下錯(cuò)誤:

  PS E:\Code\Node\demos\03-callback> node .\index.js

  finished

  Copy finished

  PS E:\Code\Node\demos\03-callback> node .\index.js

  錯(cuò)誤:ENOENT: no such file or directory, open 'E:\Code\Node\demos\03-callback\to.txt'

  Copy finished

  即使./to.txt存在,也無(wú)法讀取其中復(fù)制的內(nèi)容。

  造成這種現(xiàn)象的原因是:copyFile(...)是異步執(zhí)行的,程序執(zhí)行到copyFile(...)函數(shù)后,并不會(huì)等待其復(fù)制完畢,而是直接向下執(zhí)行,從而導(dǎo)致出現(xiàn)文件./to.txt不存在的錯(cuò)誤,或者文件內(nèi)容為空錯(cuò)誤(如果提前創(chuàng)建文件)。

  三、回調(diào)函數(shù)

  異步函數(shù)無(wú)法確定結(jié)束時(shí)間,例如readFile(from,to)函數(shù)的執(zhí)行結(jié)束時(shí)間大概率取決于文件from的大小。

  那么,問(wèn)題在于我們?nèi)绾尾拍軠?zhǔn)確的定位copyFile執(zhí)行結(jié)束,從而讀取to文件中的內(nèi)容呢?

  這就需要使用回調(diào)函數(shù),我們可以修改copyFile函數(shù)如下:

  function copyFile(from, to, callback) {
  fs.readFile(from, (err, data) => {
  if (err) {
  console.log(err.message)
  return
  }
  fs.writeFile(to, data, (err) => {
  if (err) {
  console.log(err.message)
  return
  }
  console.log('Copy finished')
  callback()//當(dāng)復(fù)制操作完成后調(diào)用回調(diào)函數(shù)
  })
  })
  }

  這樣,我們?nèi)绻枰谖募?fù)制完成后,立即執(zhí)行一些操作,就可以把這些操作寫入回調(diào)函數(shù)中:

  function copyFile(from, to, callback) {
  fs.readFile(from, (err, data) => {
  if (err) {
  console.log(err.message)
  return
  }
  fs.writeFile(to, data, (err) => {
  if (err) {
  console.log(err.message)
  return
  }
  console.log('Copy finished')
  callback()//當(dāng)復(fù)制操作完成后調(diào)用回調(diào)函數(shù)
  })
  })
  }
  copyFile('./from.txt', './to.txt', function () {
  //傳入一個(gè)回調(diào)函數(shù),讀取“to.txt”文件中的內(nèi)容并輸出
  fs.readFile('./to.txt', (err, data) => {
  if (err) {
  console.log(err.message)
  return
  }
  console.log(data.toString())
  })
  })

  如果,你已經(jīng)準(zhǔn)備好了./from.txt文件,那么以上代碼就可以直接運(yùn)行:

  PS E:\Code\Node\demos\03-callback> node .\index.js

  Copy finished

  加入社區(qū)“仙宗”,和我一起修仙吧

  社區(qū)地址:http://t.csdn.cn/EKf1h

  這種編程方式被稱為“基于回調(diào)”的異步編程風(fēng)格,異步執(zhí)行的函數(shù)應(yīng)當(dāng)提供一個(gè)回調(diào)參數(shù)用于在任務(wù)結(jié)束后調(diào)用。

  這種風(fēng)格在JavaScript編程中普遍存在,例如文件讀取函數(shù)fs.readFile、fs.writeFile都是異步函數(shù)。

  四、回調(diào)的回調(diào)

  回調(diào)函數(shù)可以準(zhǔn)確的在異步工作完成后處理后繼事宜,如果我們需要依次執(zhí)行多個(gè)異步操作,就需要嵌套回調(diào)函數(shù)。

  案例場(chǎng)景:依次讀取文件A和文件B

  代碼實(shí)現(xiàn):

  fs.readFile('./A.txt', (err, data) => {
  if (err) {
  console.log(err.message)
  return
  }
  console.log('讀取文件A:' + data.toString())
  fs.readFile('./B.txt', (err, data) => {
  if (err) {
  console.log(err.message)
  return
  }
  console.log("讀取文件B:" + data.toString())
  })
  })

  執(zhí)行效果:

  PS E:\Code\Node\demos\03-callback> node .\index.js

  讀取文件A:仙宗無(wú)限好,只是缺了佬

  讀取文件B:要想入仙宗,鏈接不能少

  http://t.csdn.cn/H1faI

  通過(guò)回調(diào)的方式,就可以在讀取文件A之后,緊接著讀取文件B。

  如果我們還想在文件B之后,繼續(xù)讀取文件C呢?這就需要繼續(xù)嵌套回調(diào): 

 fs.readFile('./A.txt', (err, data) => {//第一次回調(diào)
  if (err) {
  console.log(err.message)
  return
  }
  console.log('讀取文件A:' + data.toString())
  fs.readFile('./B.txt', (err, data) => {//第二次回調(diào)
  if (err) {
  console.log(err.message)
  return
  }
  console.log("讀取文件B:" + data.toString())
  fs.readFile('./C.txt',(err,data)=>{//第三次回調(diào)
  ...
  })
  })
  })

  現(xiàn)在我們總結(jié)下,如果我們想要依次執(zhí)行多個(gè)異步操作,需要多層嵌套回調(diào),這在層數(shù)較少時(shí)是行之有效的,但是當(dāng)嵌套次數(shù)過(guò)多時(shí),會(huì)出現(xiàn)一些問(wèn)題。

  回調(diào)的約定

  實(shí)際上,fs.readFile中的回調(diào)函數(shù)的樣式并非個(gè)例,而是JavaScript中的普遍約定。我們?nèi)蘸髸?huì)自定義大量的回調(diào)函數(shù),也需要遵守這種約定,形成良好的編碼習(xí)慣。

  約定是:

  callback的第一個(gè)參數(shù)是為 error 而保留的。一旦出現(xiàn) error,callback(err)就會(huì)被調(diào)用。

  第二個(gè)以及后面的參數(shù)用于接收異步操作的成功結(jié)果。此時(shí)callback(null, result1, result2,...)就會(huì)被調(diào)用。

  基于以上約定,一個(gè)回調(diào)函數(shù)擁有錯(cuò)誤處理和結(jié)果接收兩個(gè)功能,例如fs.readFile('...',(err,data)=>{})的回調(diào)函數(shù)就遵循了這種約定。

  五、回調(diào)地獄

  如果我們不深究的話,基于回調(diào)的異步方法處理似乎是相當(dāng)完美的處理方式。問(wèn)題在于,如果我們有一個(gè)接一個(gè) 的異步行為,那么代碼就會(huì)變成這樣:

  fs.readFile('./a.txt',(err,data)=>{
  if(err){
  console.log(err.message)
  return
  }
  //讀取結(jié)果操作
  fs.readFile('./b.txt',(err,data)=>{
  if(err){
  console.log(err.message)
  return
  }
  //讀取結(jié)果操作
  fs.readFile('./c.txt',(err,data)=>{
  if(err){
  console.log(err.message)
  return
  }
  //讀取結(jié)果操作
  fs.readFile('./d.txt',(err,data)=>{
  if(err){
  console.log(err.message)
  return
  }
  ...
  })
  })
  })
  })

  以上代碼的執(zhí)行內(nèi)容是:

  讀取文件a.txt,如果沒(méi)有發(fā)生錯(cuò)誤的話;

  讀取文件b.txt,如果沒(méi)有發(fā)生錯(cuò)誤的話;

  讀取文件c.txt,如果沒(méi)有發(fā)生錯(cuò)誤的話;

  讀取文件d.txt,…

  隨著調(diào)用的增加,代碼嵌套層級(jí)越來(lái)越深,包含越來(lái)越多的條件語(yǔ)句,從而形成不斷向右縮進(jìn)的混亂代碼,難以閱讀和維護(hù)。

  我們稱這種不斷向右增長(zhǎng)(向右縮進(jìn))的現(xiàn)象為“回調(diào)地獄”或者“末日金字塔”!

  fs.readFile('a.txt',(err,data)=>{
  fs.readFile('b.txt',(err,data)=>{
  fs.readFile('c.txt',(err,data)=>{
  fs.readFile('d.txt',(err,data)=>{
  fs.readFile('e.txt',(err,data)=>{
  fs.readFile('f.txt',(err,data)=>{
  fs.readFile('g.txt',(err,data)=>{
  fs.readFile('h.txt',(err,data)=>{
  ...
  /*
  通往地獄的大門
  ===>
  */
  })
  })
  })
  })
  })
  })
  })
  })

  上面看起來(lái)很規(guī)整,但并不是實(shí)用,主要是由于在通常業(yè)務(wù)邏輯中會(huì)有大量的條件語(yǔ)句、數(shù)據(jù)處理操作等代碼,從而打亂當(dāng)前美好的秩序,讓代碼變的難以維護(hù)。

  現(xiàn)在我們就找到了最優(yōu)解,Promise。

  六、總結(jié)

  其實(shí)這篇文章講的幾乎是異步和回調(diào)的基本概念,二者是JavaScript的核心內(nèi)容,要大家投入更多精力去解決。

  異步、并行、同步的基本概念;

  使用回調(diào)函數(shù)處理異步任務(wù);

  回調(diào)函數(shù)的嵌套和約定;

  回調(diào)地獄的基本概念;

  本篇文章講到這里,歡迎繼續(xù)關(guān)注更多精彩內(nèi)容!


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

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

相關(guān)文章

  • 《netty實(shí)戰(zhàn)》閱讀筆記(1)——Netty 念及體系結(jié)構(gòu)

    摘要:它使用了事件通知以確定在一組非阻塞套接字中有哪些已經(jīng)就緒能夠進(jìn)行相關(guān)的操作。目前,可以把看作是傳入入站或者傳出出站數(shù)據(jù)的載體。出站事件是未來(lái)將會(huì)觸發(fā)的某個(gè)動(dòng)作的操作結(jié)果,這些動(dòng)作包括打開(kāi)或者關(guān)閉到遠(yuǎn)程節(jié)點(diǎn)的連接將數(shù)據(jù)寫到或者沖刷到套接字。 netty的概念 定義 Netty 是一款異步的事件驅(qū)動(dòng)的網(wǎng)絡(luò)應(yīng)用程序框架,支持快速地開(kāi)發(fā)可維護(hù)的高性能的面向協(xié)議的服務(wù)器和客戶端。我們可以很簡(jiǎn)單的...

    solocoder 評(píng)論0 收藏0
  • RxJava系列二(基本念及使用介紹)

    摘要:作用默認(rèn)的,直接在當(dāng)前線程運(yùn)行總是開(kāi)啟一個(gè)新線程用于密集型任務(wù),如異步阻塞操作,這個(gè)調(diào)度器的線程池會(huì)根據(jù)需要增長(zhǎng)對(duì)于普通的計(jì)算任務(wù),請(qǐng)使用默認(rèn)是一個(gè),很像一個(gè)有線程緩存的新線程調(diào)度器計(jì)算所使用的。這個(gè)使用的固定的線程池,大小為核數(shù)。 轉(zhuǎn)載請(qǐng)注明出處:https://zhuanlan.zhihu.com/p/20687307 RxJava系列1(簡(jiǎn)介) RxJava系列2(基本概念及使...

    Profeel 評(píng)論0 收藏0
  • 前端知識(shí)點(diǎn)整理

    摘要:難怪超過(guò)三分之一的開(kāi)發(fā)人員工作需要一些知識(shí)。但是隨著行業(yè)的飽和,初中級(jí)前端就業(yè)形勢(shì)不容樂(lè)觀。整個(gè)系列的文章大概有篇左右,從我是如何成為一個(gè)前端工程師,到各種前端框架的知識(shí)。 為什么 call 比 apply 快? 這是一個(gè)非常有意思的問(wèn)題。 作者會(huì)在參數(shù)為3個(gè)(包含3)以內(nèi)時(shí),優(yōu)先使用 call 方法進(jìn)行事件的處理。而當(dāng)參數(shù)過(guò)多(多余3個(gè))時(shí),才考慮使用 apply 方法。 這個(gè)的原因...

    Lowky 評(píng)論0 收藏0
  • 前端知識(shí)點(diǎn)整理

    摘要:難怪超過(guò)三分之一的開(kāi)發(fā)人員工作需要一些知識(shí)。但是隨著行業(yè)的飽和,初中級(jí)前端就業(yè)形勢(shì)不容樂(lè)觀。整個(gè)系列的文章大概有篇左右,從我是如何成為一個(gè)前端工程師,到各種前端框架的知識(shí)。 為什么 call 比 apply 快? 這是一個(gè)非常有意思的問(wèn)題。 作者會(huì)在參數(shù)為3個(gè)(包含3)以內(nèi)時(shí),優(yōu)先使用 call 方法進(jìn)行事件的處理。而當(dāng)參數(shù)過(guò)多(多余3個(gè))時(shí),才考慮使用 apply 方法。 這個(gè)的原因...

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

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

0條評(píng)論

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