Node.js 阻塞與非阻塞的實現(xiàn)
1、簡介
本概述介紹了Node.js中阻塞和非阻塞調(diào)用之間的區(qū)別。本概述將參考事件循環(huán)和libuv,但不需要事先了解這些主題。假設(shè)讀者對JavaScript語言和Node.js回調(diào)模式有基本的理解。
“I/O”主要指與libuv支持的系統(tǒng)磁盤和網(wǎng)絡(luò)的交互。
2、阻塞
阻塞是指Node.js進程中額外JavaScript的執(zhí)行必須等待非JavaScript操作完成。發(fā)生這種情況是因為在發(fā)生阻塞操作時,事件循環(huán)無法繼續(xù)運行JavaScript。
在Node.js中,由于CPU密集型,而不是等待非JavaScript操作(如I/O),而表現(xiàn)出較差性能的JavaScript通常不被稱為阻塞。Node.js標(biāo)準(zhǔn)庫中使用libuv的同步方法是最常用的阻塞操作。原生模塊也可能具有阻塞方法。
Node.js標(biāo)準(zhǔn)庫中的所有I/O方法都提供非阻塞的異步版本,并接受回調(diào)函數(shù)。有些方法也有阻塞的對應(yīng)方法,它們的名稱以Sync結(jié)尾。
3、對比代碼
阻塞方法同步執(zhí)行,非阻塞方法異步執(zhí)行。
以文件系統(tǒng)模塊為例,這是一個同步文件讀?。?/p>
const fs = require('fs'); const data = fs.readFileSync('/file.md'); // blocks here until file is read
一個異步示例:
const fs = require('fs'); fs.readFile('/file.md', (err, data) => { if (err) throw err; });
第一個例子看起來比第二個簡單,但缺點是第二行會阻止任何附加JavaScript的執(zhí)行,直到整個文件被讀取。請注意,在同步版本中,如果拋出錯誤,則需要捕獲錯誤,否則進程將崩潰。在異步版本中,由作者決定是否應(yīng)該拋出錯誤。
我們稍微擴展一下我們的示例:
const fs = require('fs'); const data = fs.readFileSync('/file.md'); // blocks here until file is read console.log(data); moreWork(); // will run after console.log
一個類似但不等價的異步示例:
const fs = require('fs'); fs.readFile('/file.md', (err, data) => { if (err) throw err; console.log(data); }); moreWork(); // will run before console.log
在上面的第一個例子中,console.log將在moreWork()之前調(diào)用。在第二個例子中,fs.readFile()是非阻塞的,因此JavaScript可以繼續(xù)執(zhí)行,并將首先調(diào)用moreWork()。在不等待文件讀取完成的情況下運行moreWork()的能力是實現(xiàn)更高吞吐量的關(guān)鍵設(shè)計選擇。
4、并發(fā)性和吞吐量
Node.js中的JavaScript執(zhí)行是單線程的,因此并發(fā)是指事件循環(huán)在完成其他工作后執(zhí)行JavaScript回調(diào)函數(shù)的能力。任何期望以并發(fā)方式運行的代碼都必須允許事件循環(huán)在非JavaScript操作(如I/O)發(fā)生時繼續(xù)運行。
作為一個例子,讓我們考慮這樣一種情況,即對web服務(wù)器的每個請求需要50毫秒才能完成,其中45毫秒是可以異步完成的數(shù)據(jù)庫I/O。選擇非阻塞異步操作可以為每個請求釋放45ms的時間來處理其他請求。僅僅通過選擇使用非阻塞方法而不是阻塞方法,這在容量上是一個顯著的差異。
事件循環(huán)不同于許多其他語言中的模型,在這些語言中可以創(chuàng)建額外的線程來處理并發(fā)工作。
5、混合阻塞和非阻塞代碼
在處理I/O時,應(yīng)該避免一些方式。讓我們看一個例子:
const fs = require('fs'); fs.readFile('/file.md', (err, data) => { if (err) throw err; console.log(data); }); fs.unlinkSync('/file.md');
在上面的例子中,fs.unlinkSync()可能在fs.readFile()之前運行,這將在實際讀取file.md之前刪除它。
更好的方法是,它完全不阻塞,并保證以正確的順序執(zhí)行:
const fs = require('fs'); fs.readFile('/file.md', (readFileErr, data) => { if (readFileErr) throw readFileErr; console.log(data); fs.unlink('/file.md', unlinkErr => { if (unlinkErr) throw unlinkErr; }); });
上面在fs.readFile()的回調(diào)中放置了對fs.unlink()的非阻塞調(diào)用,這保證了操作的正確順序。
到此這篇關(guān)于Node.js 阻塞與非阻塞的實現(xiàn)的文章就介紹到這了,更多相關(guān)Node 阻塞與非阻塞內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
利用Mongoose讓JSON數(shù)據(jù)直接插入或更新到MongoDB
這篇文章主要給大家介紹了利用Mongoose讓JSON數(shù)據(jù)直接插入或更新到MongoDB數(shù)據(jù)庫的相關(guān)資料,文中詳細介紹了配置Mongoose、創(chuàng)建目錄及文件、插入數(shù)據(jù),POST提交JSON增加一條記錄以及詢數(shù)據(jù),取出剛增加的記錄等內(nèi)容,需要的朋友可以參考下。2017-05-05Node.js中的EventEmitter類使用小結(jié)
EventEmitter 是 Node.js 中的一個核心模塊,它提供了一種實現(xiàn)事件驅(qū)動編程的機制,它是一個基于觀察者模式的類,用于在應(yīng)用程序中處理事件和觸發(fā)事件,這篇文章主要介紹了Node.js中的EventEmitter類介紹,需要的朋友可以參考下2023-12-12輕松創(chuàng)建nodejs服務(wù)器(4):路由
這篇文章主要介紹了輕松創(chuàng)建nodejs服務(wù)器(4):路由,服務(wù)器需要根據(jù)不同的URL或請求來執(zhí)行不一樣的操作,我們可以通過路由來實現(xiàn)這個步驟,需要的朋友可以參考下2014-12-12