最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

整理幾個關(guān)鍵節(jié)點深入理解nodejs

 更新時間:2022年07月05日 16:09:09   作者:??小p????  
這篇文章主要介紹了整理幾個關(guān)鍵節(jié)點深入理解nodejs,文章圍繞主題展開詳細的內(nèi)容介紹,需要的小伙伴可以參考一下,需要的小伙伴可以參考一下

前言

本文是個人在實際開發(fā)和學習中對nodejs的一些理解,現(xiàn)整理出來方便日后查閱,如果能給您啟發(fā)將不勝榮幸。

非阻塞I/O

I/O:即 Input / Output,一個系統(tǒng)的輸入和輸出。

一個系統(tǒng)可以理解為一個個體,比如說一個人,你說話就是輸出,你聽就是輸入。

阻塞 I/O 與非阻塞 I/O 的區(qū)別就在于系統(tǒng)接收輸入再到輸出期間,能不能接收其他輸入。

下面以兩個例子來說明什么是阻塞 I/O 和非阻塞 I/O:

打飯

首先我們要確定一個系統(tǒng)的范圍,在這個例子中食堂阿姨和餐廳的服務生看成是一個系統(tǒng),輸入就是點菜,輸出就是端菜

那么在點菜和端菜之間能不能接受其他人的點菜,就可以判斷是阻塞I/O還是非阻塞I/O。

對于食堂阿姨,他在點菜的時候,是不能幫其他同學點菜的,只有這個同學點完菜端菜走了之后,才能接受下一個同學的點菜,所以食堂阿姨是阻塞I/O。

對于餐廳服務員,他可以在點完菜以后,這個客人端菜之前是可以服務下一位客人的,所以服務員是非阻塞I/O。

做家務

在洗衣服的時候,是不需要等著洗衣機旁邊的,這個時候可以去掃地和整理書桌,當整理完書桌后衣服也洗好了,這個時候去晾衣服,那么總共只需要25分鐘。

洗衣服其實就是一個非阻塞I/O,在把衣服扔進洗衣機和洗完衣服期間,你是可以干其他事情的。

非阻塞I/O之所以能提升性能,是因為它可以把不必要的等待給節(jié)省掉。

理解非阻塞I/O的要點在于

  • 確定一個進行I/O的系統(tǒng)邊界。這非常關(guān)鍵,如果把系統(tǒng)擴大,上面餐廳的例子,如果把系統(tǒng)擴大到整個餐廳,那么廚師肯定是一個阻塞 I/O。
  • 在 I/O 過程中,能不能進行其他 I/O。

nodejs的非阻塞 I/O

nodejs的非阻塞 I/O 是怎么體現(xiàn)的呢?前面說過理解非阻塞 I/O 的一個重要點是先確定一個系統(tǒng)邊界,nodejs的系統(tǒng)邊界就是主線程。

如果下面的架構(gòu)圖按照線程的維護劃分,左邊虛線部分是nodejs線程,右邊虛線部分是c++線程。

現(xiàn)在 nodejs 線程需要去查詢數(shù)據(jù)庫,這是一個典型的 I/O 操作,它不會等待 I/O 的結(jié)果,而且繼續(xù)處理其他的操作,它會把大量的計算能力分發(fā)到其他的c++線程去計算。

等到結(jié)果出來后返回給nodejs線程,在獲得結(jié)果之前nodejs 線程還能進行其他的I/O操作,所以是非阻塞的。

nodejs 線程 相當于左邊部分是服務員,c++ 線程是廚師。

所以,node的非阻塞I/O是通過調(diào)用c++的worker threads來完成的。

那當 c++ 線程獲取結(jié)果后怎么通知 nodejs 線程呢?答案是事件驅(qū)動。

事件驅(qū)動

阻塞:I/O時進程休眠,等待I/O完成后進行下一步; 

非阻塞:I/O時函數(shù)立即返回,進程不等待I/O完成。

那怎么知道返回的結(jié)果,就需要用到事件驅(qū)動

所謂事件驅(qū)動可以理解為跟前端點擊事件一樣,我首先寫一個點擊事件,但是我不知道什么時候觸發(fā),只有觸發(fā)的時候就去讓主線程執(zhí)行事件驅(qū)動函數(shù)。

這種模式也是一種觀察者模式,就是我首先先監(jiān)聽這個事件,等觸發(fā)時我就去執(zhí)行。

那怎么實現(xiàn)事件驅(qū)動呢?答案是異步編程。

異步編程

上面說過nodejs有大量的非阻塞I/O,那么非阻塞I/O的結(jié)果是需要通過回調(diào)函數(shù)來獲取的,這種通過回調(diào)函數(shù)的方式,就是異步編程。比如下面的代碼是通過回調(diào)函數(shù)獲取結(jié)果的:

glob(__dirname+'/**/*', (err, res) => {
    result = res
    console.log('get result')
})

回調(diào)函數(shù)格式規(guī)范

nodejs的回調(diào)函數(shù)第一個參數(shù)是error,后面的參數(shù)才是結(jié)果。為什么要這么做呢?

try {
  interview(function () {
       console.log('smile')
  })
} catch(err) {
    console.log('cry', err)
}

function interview(callback) {
    setTimeout(() => {
        if(Math.random() < 0.1) {
            callback('success')
        } else {
            throw new Error('fail')
        }
    }, 500)
}

執(zhí)行之后,沒有被捕獲,錯誤被扔到了全局,導致整個nodejs程序崩潰了。

沒有被try catch捕獲是因為setTimeout重新開啟了事件循環(huán),每開啟一個事件循環(huán)就重新生一個調(diào)用棧context,try catch是屬于上一個事件循環(huán)的調(diào)用棧的,setTimeout的回調(diào)函數(shù)執(zhí)行的時候,調(diào)用棧都不一樣了,在這個新的調(diào)用棧中是沒有try catch,所以這個錯誤被扔到全局,無法捕獲。具體可以參考這一篇文章JavaScript異步隊列進行try catch時的問題解決

那么怎么辦呢?把錯誤也作為一個參數(shù):

function interview(callback) {
    setTimeout(() => {
        if(Math.random() < 0.5) {
            callback('success')
        } else {
            callback(new Error('fail'))
        }
    }, 500)
}

interview(function (res) {
    if (res instanceof Error) {
        console.log('cry')
        return
    } 
    console.log('smile')
})

但是這樣就比較麻煩,在回調(diào)中還要判斷,所以就產(chǎn)生一種約定成熟的規(guī)定,第一個參數(shù)是err,如果不存在表示執(zhí)行成功。

function interview(callback) {
    setTimeout(() => {
        if(Math.random() < 0.5) {
            callback(null, 'success')
        } else {
            callback(new Error('fail'))
        }
    }, 500)
}

interview(function (res) {
    if (res) {
        return
    } 
    console.log('smile')
})

異步流程控制

nodejs的回調(diào)寫法,不僅會帶來回調(diào)地域,還會帶來異步流程控制的問題。

異步流程控制主要是指當并發(fā)的時候,怎么來處理并發(fā)的邏輯。還是上面的例子,如果你同事面試兩家公司,只有當成功面試兩家的時候,才可以不面試第三家,那么怎么寫這個邏輯呢?需要全局頂一個一個變量count:

var count = 0
interview((err) => {
    if (err) {
        return
    }
    count++
    if (count >= 2) {
        // 處理邏輯
    }
})
interview((err) => {
    if (err) {
        return
    }
    count++
    if (count >= 2) {
        // 處理邏輯
    }
})

像上面這種寫法就非常麻煩,且難看。所以,后來就出現(xiàn)了promise,async/await的寫法。

promise

當前事件循環(huán)得不到的結(jié)果,但未來的事件循環(huán)會給你結(jié)果。很像一個渣男說的話。

promise不僅是一個渣男,還是一個狀態(tài)機:

  • pending
  • fulfilled/resolved
  • rejectd
const pro = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('2')
    }, 200)
})
console.log(pro) // 打?。篜romise { <pending> }

then & .catch

  • resolved 狀態(tài)的 promise 會調(diào)用后面的第一個 then
  • rejected 狀態(tài)的 promise 會調(diào)用后面的第一個 catch
  • 任何一個 reject 狀態(tài)且后面沒有 .catch 的 promise,都會造成瀏覽器或者 node 環(huán)境的全局錯誤。uncaught 表示未捕獲的錯誤。

執(zhí)行then或者catch會返回一個新的promise,該promise最終狀態(tài)根據(jù)then和catch的回調(diào)函數(shù)的執(zhí)行結(jié)果決定:

  • 如果回調(diào)函數(shù)始終是throw new Error,該promise是rejected狀態(tài)
  • 如果回調(diào)函數(shù)始終是return,該promise是resolved狀態(tài)
  • 但如果回調(diào)函數(shù)始終是return一個promise,該promise會和回調(diào)函數(shù)return的promise狀態(tài)保持一致。
function interview() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (Math.random() > 0.5) {
                resolve('success')
            } else {
                reject(new Error('fail'))
            }
        })
    })
}
var promise = interview()
var promise1 = promise.then(() => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('accept')
        }, 400)
    })
})

promise1的狀態(tài)是由return里面的promise的狀態(tài)決定的,也就是return里面的promise執(zhí)行完后的狀態(tài)就是promise1的狀態(tài)。這樣有什么好處呢?這樣可以解決回調(diào)地獄的問題

var promise = interview()
    .then(() => {
        return interview()
    })
    .then(() => {
        return interview()
    })
    .then(() => {
        return interview()
    })
    .catch(e => {
        console.log(e)
    })

then如果返回的promise的狀態(tài)是rejected,那么會調(diào)用后面第一個catch,后面的then就不會在調(diào)用了。記?。簉ejected調(diào)用后面的第一個catch,resolved調(diào)用后面的第一個then。

promise解決異步流程控制

如果promise僅僅是為了解決地獄回調(diào),太小看promise了,promise最主要的作用是解決異步流程控制問題。下面如果要同時面試兩家公司:

function interview() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (Math.random() > 0.5) {
                resolve('success')
            } else {
                reject(new Error('fail'))
            }
        })
    })
}
promise
    .all([interview(), interview()])
    .then(() => {
        console.log('smile')
    })
    // 如果有一家公司rejected,就catch
    .catch(() => {
        console.log('cry')
    })

async/await

sync/await到底是什么:

console.log(async function() {
    return 4
})
console.log(function() {
    return new Promise((resolve, reject) => {
        resolve(4)
    })
})

打印的結(jié)果一樣,也就是async/await是promse的語法糖而已。

我們知道try catch捕獲錯誤是依賴調(diào)用棧的,只能捕獲到調(diào)用棧以上的錯誤。但是如果使用await后能捕捉到調(diào)用棧所有函數(shù)的錯誤。即便這個錯誤是在另一個事件循環(huán)的調(diào)用棧拋出的,比如setTimeout。

改造面試代碼,可以看到代碼精簡了很多。

try {
    await interview(1)
    await interview(2)
    await interview(2)
} catch(e => {
    console.log(e)
})

如果是并行任務呢?

await Promise.all([interview(1), interview(2)])

事件循環(huán)

因為nodejs的非阻塞 I/0, 所以需要利用事件驅(qū)動的方式獲取 I/O 的結(jié)果,實現(xiàn)事件驅(qū)動拿到結(jié)果必須使用異步編程,比如回調(diào)函數(shù)。那么如何來有序的執(zhí)行這些回調(diào)函數(shù)來獲取結(jié)果呢?那就需要使用事件循環(huán)。

事件循環(huán)是實現(xiàn) nodejs 非阻塞 I/O 功能的關(guān)鍵基礎(chǔ),非阻塞I/O和事件循環(huán)都是屬于 libuv 這個c++庫提供的能力。

代碼演示:

const eventloop = {
    queue: [],
    loop() {
        while(this.queue.length) {
            const callback = this.queue.shift()
            callback()
        }
        setTimeout(this.loop.bind(this), 50)
    },
    add(callback) {
        this.queue.push(callback)
    }
}
eventloop.loop()
setTimeout(() => {
    eventloop.add(() => {
        console.log('1')
    })
}, 500)

setTimeout(() => {
	eventloop.add(() => {
		console.log('2')
	})
}, 800)

setTimeout(this.loop.bind(this), 50)保證了50ms就會去看隊列中是否有回調(diào),如果有就去執(zhí)行。這樣就形成了一個事件循環(huán)。

當然實際的事件要復雜的多,隊列也不止一個,比如有一個文件操作對列,一個時間對列。

const eventloop = {
    queue: [],
    fsQueue: [],
    timerQueue: [],
    loop() {
        while(this.queue.length) {
            const callback = this.queue.shift()
            callback()
        }
        this.fsQueue.forEach(callback => {
            if (done) {
                callback()
            }
        })
        setTimeout(this.loop.bind(this), 50)
    },
    add(callback) {
        this.queue.push(callback)
    }
}

總結(jié)

首先我們弄清楚了什么是非阻塞I/O,即遇到I/O立刻跳過執(zhí)行后面的任務,不會等待I/O的結(jié)果。當I/O處理好了之后就會調(diào)用我們注冊的事件處理函數(shù),這就叫事件驅(qū)動。實現(xiàn)事件驅(qū)動就必須要用異步編程,異步編程是nodejs中最重要的環(huán)節(jié),它從回調(diào)函數(shù)到promise,最后到async/await(使用同步的方法寫異步邏輯)。

到此這篇關(guān)于整理幾個關(guān)鍵節(jié)點深入理解nodejs的文章就介紹到這了,更多相關(guān)深入理解nodejs內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • node.js中的fs.truncateSync方法使用說明

    node.js中的fs.truncateSync方法使用說明

    這篇文章主要介紹了node.js中的fs.truncateSync方法使用說明,本文介紹了fs.truncateSync的方法說明、語法、接收參數(shù)、使用實例和實現(xiàn)源碼,需要的朋友可以參考下
    2014-12-12
  • Node.js?應用探索文件解壓縮示例詳解

    Node.js?應用探索文件解壓縮示例詳解

    這篇文章主要為大家介紹了Node.js?應用探索文件解壓縮示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-09-09
  • Node.js中的process.nextTick使用實例

    Node.js中的process.nextTick使用實例

    這篇文章主要介紹了Node.js中的process.nextTick使用實例,nextTick函數(shù)有什么用、怎么用、和setTimeout有什么區(qū)別呢,本文就講解了這些知識,需要的朋友可以參考下
    2015-06-06
  • node.js讀取文件到字符串的方法

    node.js讀取文件到字符串的方法

    這篇文章主要介紹了node.js讀取文件到字符串的方法,涉及readFileSync方法的使用技巧,需要的朋友可以參考下
    2015-06-06
  • Mac OS X 系統(tǒng)下安裝和部署Egret引擎開發(fā)環(huán)境

    Mac OS X 系統(tǒng)下安裝和部署Egret引擎開發(fā)環(huán)境

    之前的文章,我們已經(jīng)介紹了Windows 系統(tǒng)下安裝和部署Egret的開發(fā)環(huán)境,那么,這篇文檔中,我們主要介紹在Mac環(huán)境中安裝Egret以及部署
    2014-09-09
  • Node.js 實現(xiàn)遠程桌面監(jiān)控的方法步驟

    Node.js 實現(xiàn)遠程桌面監(jiān)控的方法步驟

    這篇文章主要介紹了Node.js 實現(xiàn)遠程桌面監(jiān)控的方法步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-07-07
  • node 可讀流與可寫流的運用詳解

    node 可讀流與可寫流的運用詳解

    這篇文章主要為大家介紹了node 可讀流與可寫流的運用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-09-09
  • 詳解nodejs 開發(fā)企業(yè)微信第三方應用入門教程

    詳解nodejs 開發(fā)企業(yè)微信第三方應用入門教程

    這篇文章主要介紹了詳解nodejs 開發(fā)企業(yè)微信第三方應用入門教程,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-03-03
  • Node.js 回調(diào)函數(shù)實例詳解

    Node.js 回調(diào)函數(shù)實例詳解

    這篇文章主要介紹了Node.js 回調(diào)函數(shù)實例詳解的相關(guān)資料,需要的朋友可以參考下
    2017-07-07
  • node.js學習筆記之koa框架和簡單爬蟲練習

    node.js學習筆記之koa框架和簡單爬蟲練習

    這篇文章主要介紹了node.js學習筆記之koa框架和簡單爬蟲練習,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-12-12

最新評論

星子县| 龙川县| 磐安县| 镇赉县| 惠州市| 新兴县| 资阳市| 崇礼县| 荣成市| 平潭县| 绥宁县| 尼木县| 潮州市| 商城县| 蕉岭县| 山东省| 宜城市| 泸定县| 阆中市| 玉溪市| 郧西县| 武川县| 庆阳市| 江川县| 井冈山市| 崇仁县| 法库县| 石渠县| 县级市| 晴隆县| 渝北区| 东海县| 承德县| 湘潭县| 抚顺县| 乐业县| 双流县| 获嘉县| 丹阳市| 南充市| 双江|