欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript中異步與回調(diào)的基本概念及回調(diào)地獄現(xiàn)象

 更新時(shí)間:2022年07月11日 11:12:40   作者:@魏大大  
這篇文章主要介紹了JavaScript中異步與回調(diào)的基本概念,以及回調(diào)地獄現(xiàn)象,本文主要介紹了異步和回調(diào)的基本概念,二者是JavaScript的核心內(nèi)容,需要所有熱愛(ài)JS的小伙伴深入了解,需要的朋友可以參考下

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)

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ù)寫(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è)?code>copyFile(...)函數(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ù)的具體執(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ù)如下:

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í)行一些操作,就可以把這些操作寫(xiě)入回調(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

這種編程方式被稱(chēng)為“基于回調(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)
            ...
        })
    })
})

也就是說(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ù)。

我們稱(chēng)這種不斷向右增長(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)
								  ===>
                                */
                            })
                        })
                    })
                })
            })
        })
    })
})

雖然以上代碼看起來(lái)相當(dāng)規(guī)整,但是這只是用于舉例的理想場(chǎng)面,通常業(yè)務(wù)邏輯中會(huì)有大量的條件語(yǔ)句、數(shù)據(jù)處理操作等代碼,從而打亂當(dāng)前美好的秩序,讓代碼變的難以維護(hù)。

幸運(yùn)的是,JavaScript為我們提供了多種解決途徑,Promise就是其中的最優(yōu)解。

(原諒我賣(mài)了一個(gè)關(guān)子,這篇文章太長(zhǎng)了,下篇繼續(xù)講)

六、總結(jié)

本文主要介紹了異步和回調(diào)的基本概念,二者是JavaScript的核心內(nèi)容,需要所有熱愛(ài)JS的小伙伴深入了解。

  • 異步、并行、同步的基本概念;
  • 使用回調(diào)函數(shù)處理異步任務(wù);
  • 回調(diào)函數(shù)的嵌套和約定;
  • 回調(diào)地獄的基本概念;

到此這篇關(guān)于JavaScript中異步與回調(diào)的基本概念,以及回調(diào)地獄現(xiàn)象的文章就介紹到這了,更多相關(guān)js異步回調(diào)地獄內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • javascript字典探測(cè)用戶(hù)名工具

    javascript字典探測(cè)用戶(hù)名工具

    javascript字典探測(cè)用戶(hù)名工具...
    2006-10-10
  • date.parse在IE和FF中的區(qū)別

    date.parse在IE和FF中的區(qū)別

    這個(gè)方法是很常用的,比如在驗(yàn)證輸入日期是否存在時(shí),可以使用它,如果是一個(gè)不存在的日期,則其返回值將是NaN,另外如果要比較兩個(gè)日期的先后,或是計(jì)算兩個(gè)日期相差的天數(shù) ,都可以用到。
    2010-07-07
  • 簡(jiǎn)單談?wù)凧S數(shù)組中的indexOf方法

    簡(jiǎn)單談?wù)凧S數(shù)組中的indexOf方法

    最近在工作中遇到一個(gè)小問(wèn)題,這篇文章代碼我會(huì)簡(jiǎn)化成小例子展示給大家。給大家詳細(xì)的介紹JS數(shù)組中的indexOf方法,用心看到最后會(huì)有收獲哈,有需要的朋友們下面來(lái)一起看看吧。
    2016-10-10
  • JS解析json數(shù)據(jù)并將json字符串轉(zhuǎn)化為數(shù)組的實(shí)現(xiàn)方法

    JS解析json數(shù)據(jù)并將json字符串轉(zhuǎn)化為數(shù)組的實(shí)現(xiàn)方法

    json數(shù)據(jù)在ajax實(shí)現(xiàn)異步交互時(shí)起到了很重要的作用,他可以返回請(qǐng)求的數(shù)據(jù),然后利用客戶(hù)端的js進(jìn)行解析,這一點(diǎn)體現(xiàn)出js的強(qiáng)大,本文介紹JS解析json數(shù)據(jù)并將json字符串轉(zhuǎn)化為數(shù)組的實(shí)現(xiàn)方法,需要了解的朋友可以參考下
    2012-12-12
  • JS實(shí)現(xiàn)提示框跟隨鼠標(biāo)移動(dòng)

    JS實(shí)現(xiàn)提示框跟隨鼠標(biāo)移動(dòng)

    在本篇內(nèi)容里小編給各位整理了一篇關(guān)于JS實(shí)現(xiàn)提示框跟隨鼠標(biāo)移動(dòng)的相關(guān)實(shí)例代碼,需要的朋友們學(xué)習(xí)下。
    2019-08-08
  • JS實(shí)現(xiàn)簡(jiǎn)單的天數(shù)計(jì)算器完整實(shí)例

    JS實(shí)現(xiàn)簡(jiǎn)單的天數(shù)計(jì)算器完整實(shí)例

    這篇文章主要介紹了JS實(shí)現(xiàn)簡(jiǎn)單的天數(shù)計(jì)算器,結(jié)合完整實(shí)例形式分析了javascript針對(duì)日期的獲取及天數(shù)運(yùn)算相關(guān)操作技巧,需要的朋友可以參考下
    2017-04-04
  • 新手入門(mén)js閉包學(xué)習(xí)過(guò)程解析

    新手入門(mén)js閉包學(xué)習(xí)過(guò)程解析

    這篇文章主要介紹了新手入門(mén)js閉包學(xué)習(xí)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-10-10
  • javascript如何讀寫(xiě)本地sqlite數(shù)據(jù)庫(kù)

    javascript如何讀寫(xiě)本地sqlite數(shù)據(jù)庫(kù)

    這篇文章主要介紹了javascript如何讀寫(xiě)本地sqlite數(shù)據(jù)庫(kù)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • 你可能不知道的JavaScript的new Function()方法

    你可能不知道的JavaScript的new Function()方法

    JavaScript的精神領(lǐng)袖Douglas Crockford曾說(shuō)過(guò)JavaScript是程序員唯一不需要學(xué)習(xí)就能直接使用的語(yǔ)言. 在編程中確實(shí)是如此
    2014-04-04
  • JavaScript canvas基于數(shù)組生成柱狀圖代碼實(shí)例

    JavaScript canvas基于數(shù)組生成柱狀圖代碼實(shí)例

    這篇文章主要介紹了JavaScript canvas基于數(shù)組生成柱狀圖代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-03-03

最新評(píng)論