真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Node.js中回調(diào)隊(duì)列的案例分析-創(chuàng)新互聯(lián)

這篇文章給大家分享的是有關(guān)Node.js中回調(diào)隊(duì)列的案例分析的內(nèi)容。小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧。

為白云等地區(qū)用戶提供了全套網(wǎng)頁(yè)設(shè)計(jì)制作服務(wù),及白云網(wǎng)站建設(shè)行業(yè)解決方案。主營(yíng)業(yè)務(wù)為網(wǎng)站建設(shè)、做網(wǎng)站、白云網(wǎng)站設(shè)計(jì),以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專業(yè)、用心的態(tài)度為用戶提供真誠(chéng)的服務(wù)。我們深信只要達(dá)到每一位用戶的要求,就會(huì)得到認(rèn)可,從而選擇與我們長(zhǎng)期合作。這樣,我們也可以走得更遠(yuǎn)!

隊(duì)列是 Node.js 中用于有效處理異步操作的一項(xiàng)重要技術(shù)。

在本文中,我們將深入研究 Node.js 中的隊(duì)列:它們是什么,它們?nèi)绾喂ぷ鳎ㄍㄟ^(guò)事件循環(huán))以及它們的類型。

Node.js 中的隊(duì)列是什么?

隊(duì)列是 Node.js 中用于組織異步操作的數(shù)據(jù)結(jié)構(gòu)。這些操作以不同的形式存在,包括HTTP請(qǐng)求、讀取或?qū)懭胛募僮?、流等?/p>

在 Node.js 中處理異步操作非常具有挑戰(zhàn)性。

HTTP 請(qǐng)求期間可能會(huì)出現(xiàn)不可預(yù)測(cè)的延遲(或者更糟糕的可能性是沒(méi)有結(jié)果),具體取決于網(wǎng)絡(luò)質(zhì)量。嘗試用 Node.js 讀寫(xiě)文件時(shí)也有可能會(huì)產(chǎn)生延遲,具體取決于文件的大小。

類似于計(jì)時(shí)器和其他的許多操作,異步操作完成的時(shí)間也有可能是不確定的。

在這些不同的延遲情況之下,Node.js 需要能夠有效地處理所有這些操作。

Node.js 無(wú)法處理基于 first-start-first-handle (先開(kāi)始先處理)或 first-finish-first-handle (先結(jié)束先處理)的操作。

之所以不能這樣做的一個(gè)原因是,在一個(gè)異步操作中可能還會(huì)包含另一個(gè)異步操作。

為第一個(gè)異步過(guò)程留出空間意味著必須先要完成內(nèi)部異步過(guò)程,然后才能考慮隊(duì)列中的其他異步操作。

有許多情況需要考慮,因此最好的選擇是制定規(guī)則。這個(gè)規(guī)則影響了事件循環(huán)和隊(duì)列在 Node.js 中的工作方式。

讓我們簡(jiǎn)要地看一下 Node.js 是怎樣處理異步操作的。

調(diào)用棧,事件循環(huán)和回調(diào)隊(duì)列

調(diào)用棧被用于跟蹤當(dāng)前正在執(zhí)行的函數(shù)以及從何處開(kāi)始運(yùn)行。當(dāng)一個(gè)函數(shù)將要執(zhí)行時(shí),它會(huì)被添加到調(diào)用堆棧中。這有助于 JavaScript 在執(zhí)行函數(shù)后重新跟蹤其處理步驟。

回調(diào)隊(duì)列是在后臺(tái)操作完成時(shí)把回調(diào)函數(shù)保存為異步操作的隊(duì)列。它們以先進(jìn)先出(FIFO)的方式工作。我們將會(huì)在本文后面介紹不同類型的回調(diào)隊(duì)列。

請(qǐng)注意,Node.js 負(fù)責(zé)所有異步活動(dòng),因?yàn)?JavaScript 可以利用其單線程性質(zhì)來(lái)阻止產(chǎn)生新的線程。

在完成后臺(tái)操作后,它還負(fù)責(zé)向回調(diào)隊(duì)列添加函數(shù)。 JavaScript 本身與回調(diào)隊(duì)列無(wú)關(guān)。同時(shí)事件循環(huán)會(huì)連續(xù)檢查調(diào)用棧是否為空,以便可以從回調(diào)隊(duì)列中提取一個(gè)函數(shù)并添加到調(diào)用棧中。事件循環(huán)僅在執(zhí)行所有同步操作之后才檢查隊(duì)列。

那么,事件循環(huán)是按照什么樣的順序從隊(duì)列中選擇回調(diào)函數(shù)的呢?

首先,讓我們看一下回調(diào)隊(duì)列的五種主要類型。

回調(diào)隊(duì)列的類型

IO 隊(duì)列(IO queue)

IO操作是指涉及外部設(shè)備(如計(jì)算機(jī)的硬盤(pán)、網(wǎng)卡等)的操作。常見(jiàn)的操作包括讀寫(xiě)文件操作、網(wǎng)絡(luò)操作等。這些操作應(yīng)該是異步的,因?yàn)樗鼈兞艚o Node.js 處理。

JavaScript 無(wú)法訪問(wèn)計(jì)算機(jī)的內(nèi)部設(shè)備。當(dāng)執(zhí)行此類操作時(shí),JavaScript 會(huì)將其傳輸?shù)?Node.js 以在后臺(tái)處理。

完成后,它們將會(huì)被轉(zhuǎn)移到 IO 回調(diào)隊(duì)列中,來(lái)進(jìn)行事件循環(huán),以轉(zhuǎn)移到調(diào)用棧中執(zhí)行。

計(jì)時(shí)器隊(duì)列(Timer queue)

每個(gè)涉及 Node.js 計(jì)時(shí)器功能的操作(如 setTimeout()setInterval())都是要被添加到計(jì)時(shí)器隊(duì)列的。

請(qǐng)注意,JavaScript 語(yǔ)言本身沒(méi)有計(jì)時(shí)器功能。它使用 Node.js 提供的計(jì)時(shí)器 API(包括 setTimeout )執(zhí)行與時(shí)間相關(guān)的操作。所以計(jì)時(shí)器操作是異步的。無(wú)論是 2 秒還是 0 秒,JavaScript 都會(huì)把與時(shí)間相關(guān)的操作移交給 Node.js,然后將其完成并添加到計(jì)時(shí)器隊(duì)列中。

例如:

setTimeout(function() {
        console.log('setTimeout');
    }, 0)
    console.log('yeah')


# 返回
yeah
setTimeout

在處理異步操作時(shí),JavaScript 會(huì)繼續(xù)執(zhí)行其他操作。只有在所有同步操作都已被處理完畢后,事件循環(huán)才會(huì)進(jìn)入回調(diào)隊(duì)列。

微任務(wù)隊(duì)列(Microtask queue)

該隊(duì)列分為兩個(gè)隊(duì)列:

  • 第一個(gè)隊(duì)列包含因 process.nextTick 函數(shù)而延遲的函數(shù)。

事件循環(huán)執(zhí)行的每個(gè)迭代稱為一個(gè) tick(時(shí)間刻度)。

process.nextTick 是一個(gè)函數(shù),它在下一個(gè) tick (即事件循環(huán)的下一個(gè)迭代)執(zhí)行一個(gè)函數(shù)。微任務(wù)隊(duì)列需要存儲(chǔ)此類函數(shù),以便可以在下一個(gè) tick 執(zhí)行它們。

這意味著事件循環(huán)必須繼續(xù)檢查微任務(wù)隊(duì)列中的此類函數(shù),然后再進(jìn)入其他隊(duì)列。

  • 第二個(gè)隊(duì)列包含因 promises 而延遲的函數(shù)。

如你所見(jiàn),在 IO 和計(jì)時(shí)器隊(duì)列中,所有與異步操作有關(guān)的內(nèi)容都被移交給了異步函數(shù)。

但是 promise 不同。在 promise 中,初始變量存儲(chǔ)在 JavaScript 內(nèi)存中(你可能已經(jīng)注意到了)。

異步操作完成后,Node.js 會(huì)將函數(shù)(附加到 Promise)放在微任務(wù)隊(duì)列中。同時(shí)它用得到的結(jié)果來(lái)更新 JavaScript 內(nèi)存中的變量,以使該函數(shù)不與 一起運(yùn)行。

以下代碼說(shuō)明了 promise 是如何工作的:

let prom = new Promise(function (resolve, reject) {
        // 延遲執(zhí)行
        setTimeout(function () {
            return resolve("hello");
        }, 2000);
    });
    console.log(prom);
    // Promise {  }
    
    prom.then(function (response) {
        console.log(response);
    });
    // 在 2000ms 之后,輸出
    // hello

關(guān)于微任務(wù)隊(duì)列,需要注意一個(gè)重要功能,事件循環(huán)在進(jìn)入其他隊(duì)列之前要反復(fù)檢查并執(zhí)行微任務(wù)隊(duì)列中的函數(shù)。例如,當(dāng)微任務(wù)隊(duì)列完成時(shí),或者說(shuō)計(jì)時(shí)器操作執(zhí)行了 Promise 操作,事件循環(huán)將會(huì)在繼續(xù)進(jìn)入計(jì)時(shí)器隊(duì)列中的其他函數(shù)之前參與該 Promise 操作。

因此,微任務(wù)隊(duì)列比其他隊(duì)列具有最高的優(yōu)先級(jí)。

檢查隊(duì)列(Check queue)

檢查隊(duì)列也稱為即時(shí)隊(duì)列(immediate queue)。IO 隊(duì)列中的所有回調(diào)函數(shù)均已執(zhí)行完畢后,立即執(zhí)行此隊(duì)列中的回調(diào)函數(shù)。setImmediate 用于向該隊(duì)列添加函數(shù)。

例如:

const fs = require('fs');
setImmediate(function() {
    console.log('setImmediate');
})
// 假設(shè)此操作需要 1ms
fs.readFile('path-to-file', function() {
    console.log('readFile')
})
// 假設(shè)此操作需要 3ms
do...while...

執(zhí)行該程序時(shí),Node.js 把 setImmediate 回調(diào)函數(shù)添加到檢查隊(duì)列。由于整個(gè)程序尚未準(zhǔn)備完畢,因此事件循環(huán)不會(huì)檢查任何隊(duì)列。

因?yàn)?readFile 操作是異步的,所以會(huì)移交給 Node.js,之后程序?qū)?huì)繼續(xù)執(zhí)行。

do while  操作持續(xù) 3ms。在這段時(shí)間內(nèi),readFile 操作完成并被推送到 IO 隊(duì)列。完成此操作后,事件循環(huán)將會(huì)開(kāi)始檢查隊(duì)列。

盡管首先填充了檢查隊(duì)列,但只有在 IO 隊(duì)列為空之后才考慮使用它。所以在 setImmediate 之前,將 readFile 輸出到控制臺(tái)。

關(guān)閉隊(duì)列(Close queue)

此隊(duì)列存儲(chǔ)與關(guān)閉事件操作關(guān)聯(lián)的函數(shù)。

包括以下內(nèi)容:

  • 流關(guān)閉事件,在關(guān)閉流時(shí)發(fā)出。它表示不再發(fā)出任何事件。
  • http關(guān)閉事件,在服務(wù)器關(guān)閉時(shí)發(fā)出。

這些隊(duì)列被認(rèn)為是優(yōu)先級(jí)最低的,因?yàn)榇颂幍牟僮鲿?huì)在以后發(fā)生。

你肯sing不希望在處理 promise 函數(shù)之前在 close 事件中執(zhí)行回調(diào)函數(shù)。當(dāng)服務(wù)器已經(jīng)關(guān)閉時(shí),promise 函數(shù)會(huì)做些什么呢?

隊(duì)列順序

微任務(wù)隊(duì)列具有最高優(yōu)先級(jí),其次是計(jì)時(shí)器隊(duì)列,I/O隊(duì)列,檢查隊(duì)列,最后是關(guān)閉隊(duì)列。

回調(diào)隊(duì)列的例子

讓我們通過(guò)一個(gè)更復(fù)雜的例子來(lái)說(shuō)明隊(duì)列的類型和順序:

const fs = require("fs");

// 假設(shè)此操作需要 2ms
fs.writeFile('./new-file.json', '...', function() {
    console.log('writeFile')
})

// 假設(shè)這需要 10ms 才能完成 
fs.readFile("./file.json", function(err, data) {
    console.log("readFile");
});

// 不需要假設(shè),這實(shí)際上需要 1ms
setTimeout(function() {
    console.log("setTimeout");
}, 1000);

// 假設(shè)此操作需要 3ms
while(...) {
    ...
}

setImmediate(function() {
    console.log("setImmediate");
});

// 解決 promise 需要 4 ms
let promise = new Promise(function (resolve, reject) {
    setTimeout(function () {
        return resolve("promise");
    }, 4000);
});
promise.then(function(response) {
    console.log(response)
})

console.log("last line");

程序流程如下:

  • 在 0 毫秒時(shí),程序開(kāi)始。
  • 在 Node.js 將回調(diào)函數(shù)添加到 IO 隊(duì)列之前,fs.writeFile 在后臺(tái)花費(fèi) 2 毫秒。

fs.readFile takes 10ms at the background before Node.js adds the callback function to the IO queue.

  • 在 Node.js 將回調(diào)函數(shù)添加到 IO 隊(duì)列之前,fs.readFile 在后臺(tái)花費(fèi) 10 毫秒。
  • 在 Node.js 將回調(diào)函數(shù)添加到計(jì)時(shí)器隊(duì)列之前,setTimeout 在后臺(tái)花費(fèi) 1ms。
  • 現(xiàn)在,while 操作(同步)需要 3ms。在此期間,線程被阻止(請(qǐng)記住 JavaScript 是單線程的)。
  • 同樣在這段時(shí)間內(nèi),setTimeoutfs.writeFile 操作完成,并將它們的回調(diào)函數(shù)分別添加到計(jì)時(shí)器和 IO 隊(duì)列中。

現(xiàn)在的隊(duì)列是:

// queues
Timer = [
    function () {
        console.log("setTimeout");
    },
];
IO = [
    function () {
        console.log("writeFile");
    },
];

setImmediate 將回調(diào)函數(shù)添加到 Check 隊(duì)列中:

js
// 隊(duì)列
Timer...
IO...
Check = [
    function() {console.log("setImmediate")}
]

在將 promise 操作添加到微任務(wù)隊(duì)列之前,需要花費(fèi) 4ms 的時(shí)間在后臺(tái)進(jìn)行解析。

最后一行是同步的,因此將會(huì)立即執(zhí)行:

# 返回
"last line"

因?yàn)樗型交顒?dòng)都已完成,所以事件循環(huán)開(kāi)始檢查隊(duì)列。由于微任務(wù)隊(duì)列為空,因此它從計(jì)時(shí)器隊(duì)列開(kāi)始:

// 隊(duì)列
Timer = [] // 現(xiàn)在是空的
IO...
Check...


# 返回
"last line"
"setTimeout"

當(dāng)事件循環(huán)繼續(xù)執(zhí)行隊(duì)列中的回調(diào)函數(shù)時(shí),promise 操作完成并被添加到微任務(wù)隊(duì)列中:

// 隊(duì)列
    Timer = [];
    Microtask = [
        function (response) {
            console.log(response);
        },
    ];
    IO = []; // 當(dāng)前是空的
    Check = []; // 當(dāng)前是在 IO 的后面,為空


    # results
    "last line"
    "setTimeout"
    "writeFile"
    "setImmediate"

幾秒鐘后,readFile 操作完成,并添加到 IO 隊(duì)列中:

// 隊(duì)列
    Timer = [];
    Microtask = []; // 當(dāng)前是空的
    IO = [
        function () {
            console.log("readFile");
        },
    ];
    Check = [];


    # results
    "last line"
    "setTimeout"
    "writeFile"
    "setImmediate"
    "promise"

最后,執(zhí)行所有回調(diào)函數(shù):

// 隊(duì)列
    Timer = []
    Microtask = []
    IO = [] // 現(xiàn)在又是空的
    Check = [];


    # results
    "last line"
    "setTimeout"
    "writeFile"
    "setImmediate"
    "promise"
    "readFile"

這里要注意的三點(diǎn):

  • 異步操作取決于添加到隊(duì)列之前的延遲時(shí)間。并不取決于它們?cè)诔绦蛑械拇娣彭樞颉?/li>
  • 事件循環(huán)在每次迭代之繼續(xù)檢查其他任務(wù)之前,會(huì)連續(xù)檢查微任務(wù)隊(duì)列。
  • 即使在后臺(tái)有另一個(gè) IO 操作(readFile),事件循環(huán)也會(huì)執(zhí)行檢查隊(duì)列中的函數(shù)。這樣做的原因是此時(shí) IO 隊(duì)列為空。請(qǐng)記住,在執(zhí)行 IO 隊(duì)列中的所有的函數(shù)之后,將會(huì)立即運(yùn)行檢查隊(duì)列回調(diào)。

總結(jié)

JavaScript 是單線程的。每個(gè)異步函數(shù)都由依賴操作系統(tǒng)內(nèi)部函數(shù)工作的 Node.js 去處理。

Node.js 負(fù)責(zé)將回調(diào)函數(shù)(通過(guò) JavaScript 附加到異步操作)添加到回調(diào)隊(duì)列中。事件循環(huán)會(huì)確定將要在每次迭代中接下來(lái)要執(zhí)行的回調(diào)函數(shù)。

了解隊(duì)列如何在 Node.js 中工作,使你對(duì)其有了更好的了解,因?yàn)殛?duì)列是環(huán)境的核心功能之一。 Node.js 最受歡迎的定義是 non-blocking(非阻塞),這意味著異步操作可以被正確的處理。都是因?yàn)橛辛耸录h(huán)和回調(diào)隊(duì)列才能使此功能生效。

感謝各位的閱讀!關(guān)于Node.js中回調(diào)隊(duì)列的案例分析就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,讓大家可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到吧!


網(wǎng)頁(yè)名稱:Node.js中回調(diào)隊(duì)列的案例分析-創(chuàng)新互聯(lián)
文章URL:http://weahome.cn/article/cogshp.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部