JavaScript中異步與回調(diào)的基本概念及回調(diào)地獄現(xiàn)象_第1頁(yè)
JavaScript中異步與回調(diào)的基本概念及回調(diào)地獄現(xiàn)象_第2頁(yè)
JavaScript中異步與回調(diào)的基本概念及回調(diào)地獄現(xiàn)象_第3頁(yè)
JavaScript中異步與回調(diào)的基本概念及回調(diào)地獄現(xiàn)象_第4頁(yè)
JavaScript中異步與回調(diào)的基本概念及回調(diào)地獄現(xiàn)象_第5頁(yè)
已閱讀5頁(yè),還剩5頁(yè)未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

第JavaScript中異步與回調(diào)的基本概念及回調(diào)地獄現(xiàn)象目錄JavaScript異步與回調(diào)一、前言二、異步函數(shù)三、回調(diào)函數(shù)四、回調(diào)的回調(diào)五、回調(diào)地獄六、總結(jié)

JavaScript異步與回調(diào)

一、前言

在學(xué)習(xí)本文內(nèi)容之前,我們必須要先了解異步的概念,首先要強(qiáng)調(diào)的是異步和并行有著本質(zhì)的區(qū)別。

并行,一般指并行計(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è)線程參與。

也許用圖片的方式解釋并行、同步和異步更為直觀,假設(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):

constfs=require('fs')

functioncopyFile(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('Copyfinished')

}

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

我們可以像這樣調(diào)用copyFile:

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

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

copyFile('./from.txt','./to.txt')

//下面的代碼不會(huì)等待上面的代碼執(zhí)行結(jié)束

...

執(zhí)行到這里,好像一切還都是正常的,但是,如果我們?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ò)誤:

PSE:\Code\Node\demos\03-callbacknode.\index.js

finished

Copyfinished

PSE:\Code\Node\demos\03-callbacknode.\index.js

錯(cuò)誤:ENOENT:nosuchfileordirectory,openE:\Code\Node\demos\03-callback\to.txt

Copyfinished

即使./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ù)的具體執(zhí)行結(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ù)如下:

functioncopyFile(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('Copyfinished')

callback()//當(dāng)復(fù)制操作完成后調(diào)用回調(diào)函數(shù)

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

functioncopyFile(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('Copyfinished')

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)行:

PSE:\Code\Node\demos\03-callbacknode.\index.js

Copyfinished

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

社區(qū)地址:/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í)行效果:

PSE:\Code\Node\demos\03-callbacknode.\index.js

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

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

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

})

也就是說(shuō),如果我們想要依次執(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)={

通往地獄的大門(mén)

===

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論