中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

JavaScript中異步與回調的基本概念是什么

發布時間:2022-07-11 13:47:13 來源:億速云 閱讀:114 作者:iii 欄目:web開發

本篇內容介紹了“JavaScript中異步與回調的基本概念是什么”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

JavaScript中異步與回調的基本概念是什么

一、前言

在學習本文內容之前,我們必須要先了解異步的概念,首先要強調的是異步和并行有著本質的區別

  • 并行,一般指并行計算,是說同一時刻有多條指令同時被執行,這些指令可能執行于同一CPU的多核上,或者多個CPU上,或者多個物理主機甚至多個網絡中。

  • 同步,一般指按照預定的順序依次執行任務,只有當上一個任務完成后,才開始執行下一個任務。

  • 異步,與同步相對應,異步指的是讓CPU暫時擱置當前任務,先處理下一個任務,當收到上個任務的回調通知后,再返回上個任務繼續執行,整個過程無需第二個線程參與

也許用圖片的方式解釋并行、同步和異步更為直觀,假設現在有A、B兩個任務需要處理,使用并行、同步和異步的處理方式會分別采用如下圖所示的執行方式:

JavaScript中異步與回調的基本概念是什么

二、異步函數

JavaScript為我們提供了許多異步的函數,這些函數允許我們方便的執行異步任務,也就是說,我們現在開始執行一個任務(函數),但任務會在稍后完成,具體完成時間并不清楚。

例如,setTimeout函數就是一個非常典型的異步函數,此外,fs.readFilefs.writeFile同樣也是異步函數。

我們可以自己定義一個異步任務的案例,例如自定義一個文件復制函數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')
        })
    })}

函數copyFile首先從參數from讀取文件數據,隨后將數據寫入參數to指向的文件。

我們可以像這樣調用copyFile

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

如果這個時候,copyFile(...)后面還有其他代碼,那么程序不會等待copyFile執行結束,而是直接向下執行,文件復制任務何時結束,程序并不關心。

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

執行到這里,好像一切還都是正常的,但是,如果我們在copyFile(...)函數后,直接訪問文件./to.txt中的內容會發生什么呢?

這將不會讀到復制過來的內容,就行這樣:

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

如果在執行程序之前,./to.txt文件還沒有創建,將得到如下錯誤:

PS E:\Code\Node\demos\03-callback> node .\index.js
finished
Copy finished
PS E:\Code\Node\demos\03-callback> node .\index.js
錯誤:ENOENT: no such file or directory, open 'E:\Code\Node\demos\03-callback\to.txt'Copy finished

即使./to.txt存在,也無法讀取其中復制的內容。

造成這種現象的原因是:copyFile(...)是異步執行的,程序執行到copyFile(...)函數后,并不會等待其復制完畢,而是直接向下執行,從而導致出現文件./to.txt不存在的錯誤,或者文件內容為空錯誤(如果提前創建文件)。

三、回調函數

異步函數的具體執行結束的時間是不能確定的,例如readFile(from,to)函數的執行結束時間大概率取決于文件from的大小。

那么,問題在于我們如何才能準確的定位copyFile執行結束,從而讀取to文件中的內容呢?

這就需要使用回調函數,我們可以修改copyFile函數如下:

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()//當復制操作完成后調用回調函數
        })
    })}

這樣,我們如果需要在文件復制完成后,立即執行一些操作,就可以把這些操作寫入回調函數中:

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()//當復制操作完成后調用回調函數
        })
    })}copyFile('./from.txt', './to.txt', function () {
    //傳入一個回調函數,讀取“to.txt”文件中的內容并輸出
    fs.readFile('./to.txt', (err, data) => {
        if (err) {
            console.log(err.message)
            return
        }
        console.log(data.toString())
    })})

如果,你已經準備好了./from.txt文件,那么以上代碼就可以直接運行:

PS E:\Code\Node\demos\03-callback> node .\index.js
Copy finished
加入社區“仙宗”,和我一起修仙吧
社區地址:http://t.csdn.cn/EKf1h

這種編程方式被稱為“基于回調”的異步編程風格,異步執行的函數應當提供一個回調參數用于在任務結束后調用。

這種風格在JavaScript編程中普遍存在,例如文件讀取函數fs.readFilefs.writeFile都是異步函數。

四、回調的回調

回調函數可以準確的在異步工作完成后處理后繼事宜,如果我們需要依次執行多個異步操作,就需要嵌套回調函數。

案例場景:依次讀取文件A和文件B

代碼實現:

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

執行效果:

PS E:\Code\Node\demos\03-callback> node .\index.js
讀取文件A:仙宗無限好,只是缺了佬

讀取文件B:要想入仙宗,鏈接不能少  
http://t.csdn.cn/H1faI

通過回調的方式,就可以在讀取文件A之后,緊接著讀取文件B。

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

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())
        fs.readFile('./C.txt',(err,data)=>{//第三次回調
            ...
        })
    })})

也就是說,如果我們想要依次執行多個異步操作,需要多層嵌套回調,這在層數較少時是行之有效的,但是當嵌套次數過多時,會出現一些問題。

回調的約定

實際上,fs.readFile中的回調函數的樣式并非個例,而是JavaScript中的普遍約定。我們日后會自定義大量的回調函數,也需要遵守這種約定,形成良好的編碼習慣。

約定是:

  1. callback 的第一個參數是為 error 而保留的。一旦出現 error,callback(err) 就會被調用。

  2. 第二個以及后面的參數用于接收異步操作的成功結果。此時 callback(null, result1, result2,...) 就會被調用。

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

五、回調地獄

如果我們不深究的話,基于回調的異步方法處理似乎是相當完美的處理方式。問題在于,如果我們有一個接一個 的異步行為,那么代碼就會變成這樣:

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

以上代碼的執行內容是:

  1. 讀取文件a.txt,如果沒有發生錯誤的話;

  2. 讀取文件b.txt,如果沒有發生錯誤的話;

  3. 讀取文件c.txt,如果沒有發生錯誤的話;

  4. 讀取文件d.txt,…

隨著調用的增加,代碼嵌套層級越來越深,包含越來越多的條件語句,從而形成不斷向右縮進的混亂代碼,難以閱讀和維護。

我們稱這種不斷向右增長(向右縮進)的現象為“回調地獄”或者“末日金字塔”!

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)=>{
                                ...
                                /*
								  通往地獄的大門
								  ===>
                                */
                            })
                        })
                    })
                })
            })
        })
    })})

雖然以上代碼看起來相當規整,但是這只是用于舉例的理想場面,通常業務邏輯中會有大量的條件語句、數據處理操作等代碼,從而打亂當前美好的秩序,讓代碼變的難以維護。幸運的是,JavaScript為我們提供了多種解決途徑,Promise就是其中的最優解。

“JavaScript中異步與回調的基本概念是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

黑龙江省| 缙云县| 吉木乃县| 镇巴县| 锦州市| 将乐县| 明水县| 肇东市| 顺义区| 红原县| 崇文区| 井研县| 萍乡市| 芷江| 调兵山市| 富宁县| 泊头市| 塔河县| 平邑县| 濮阳市| 宝丰县| 阿拉尔市| 安平县| 南漳县| 斗六市| 洪江市| 邹城市| 玉山县| 灵川县| 隆林| 鄂托克旗| 乌恰县| 宣城市| 吴桥县| 海原县| 米泉市| 菏泽市| 綦江县| 承德县| 乐平市| 思南县|