1. 程式人生 > 程式設計 >詳解Node.JS模組 process

詳解Node.JS模組 process

process 模組是 nodejs 提供給開發者用來和當前程序互動的工具,它的提供了很多實用的 API。從文件出發,管中窺豹,進一步認識和學習 process 模組:

  • 如何處理命令引數?
  • 如何處理工作目錄?
  • 如何處理異常?
  • 如何處理程序退出?
  • process 的標準流物件
  • 深入理解 process.nextTick

如何處理命令引數?

命令列引數指的是 2 個方面:

  • 傳給 node 的引數。例如 node --harmony script.js --version 中,--harmony 就是傳給 node 的引數
  • 傳給程序的引數。例如 node script.js --version --help
    中,--version --help 就是傳給程序的引數

它們分別通過 process.argvprocess.execArgv 來獲得。

如何處理工作目錄?

通過process.cwd()可以獲取當前的工作目錄。

通過process.chdir(directory)可以切換當前的工作目錄,失敗後會丟擲異常。實踐如下:

function safeChdir(dir) {
 try {
  process.chdir(dir);
  return true;
 } catch (error) {
  return false;
 }
}

如何處理異常?

uncaughtException 事件

Nodejs 可以通過 try-catch 來捕獲異常。如果異常未捕獲,則會一直從底向事件迴圈冒泡。如是冒泡到事件迴圈的異常沒被處理,那麼就會導致當前程序異常退出。

根據文件,可以通過監聽 process 的 uncaughtException 事件,來處理未捕獲的異常:

process.on("uncaughtException",(err,origin) => {
 console.log(err.message);
});

const a = 1 / b;
console.log("abc"); // 不會執行

上面的程式碼,控制檯的輸出是:b is not defined。捕獲了錯誤資訊,並且程序以0退出。開發者可以在 uncaughtException 事件中,清除一些已經分配的資源(檔案描述符、控制代碼等),不推薦在其中重啟程序。

unhandledRejection 事件

如果一個 Promise 回撥的異常沒有被.catch()捕獲,那麼就會觸發 process 的 unhandledRejection 事件:

process.on("unhandledRejection",promise) => {
 console.log(err.message);
});

Promise.reject(new Error("錯誤資訊")); // 未被catch捕獲的異常,交由unhandledRejection事件處理

warning 事件

告警不是 Node.js 和 Javascript 錯誤處理流程的正式組成部分。 一旦探測到可能導致應用效能問題,缺陷或安全隱患相關的程式碼實踐,Node.js 就可發出告警。

比如前一段程式碼中,如果出現未被捕獲的 promise 回撥的異常,那麼就會觸發 warning 事件。

如何處理程序退出?

process.exit() vs process.exitCode

一個 nodejs 程序,可以通過 process.exit() 來指定退出程式碼,直接退出。不推薦直接使用 process.exit(),這會導致事件迴圈中的任務直接不被處理,以及可能導致資料的截斷和丟失(例如 stdout 的寫入)。

setTimeout(() => {
 console.log("我不會執行");
});

process.exit(0);

正確安全的處理是,設定 process.exitCode,並允許程序自然退出。

setTimeout(() => {
 console.log("我不會執行");
});

process.exitCode = 1;

beforeExit 事件

用於處理程序退出的事件有:beforeExit 事件 和 exit 事件。

當 Node.js 清空其事件迴圈並且沒有其他工作要安排時,會觸發 beforeExit 事件。例如在退出前需要一些非同步操作,那麼可以寫在 beforeExit 事件中:

let hasSend = false;
process.on("beforeExit",() => {
 if (hasSend) return; // 避免死迴圈

 setTimeout(() => {
  console.log("mock send data to serve");
  hasSend = true;
 },500);
});

console.log(".......");
// 輸出:
// .......
// mock send data to serve

注意:在 beforeExit 事件中如果是非同步任務,那麼又會被新增到任務佇列。此時,任務佇列完成所有任務後,又回觸發 beforeExit 事件。因此,不處理的話,可能出現死迴圈的情況。如果是顯式呼叫 exit(),那麼不會觸發此事件。

exit 事件

在 exit 事件中,只能執行同步操作。在呼叫 ‘exit' 事件監聽器之後,Node.js 程序將立即退出,從而導致在事件迴圈中仍排隊的任何其他工作被放棄。

process 的標準流物件

process 提供了 3 個標準流。需要注意的是,它們有些在某些時候是同步阻塞的(請見文件)。

  • process.stderr:WriteStream 型別,console.error的底層實現,預設對應螢幕
  • process.stdout:WriteStream 型別,console.log的底層實現,預設對應螢幕
  • process.stdin:ReadStream 型別,預設對應鍵盤輸入

下面是基於“生產者-消費者模型”的讀取控制檯輸入並且及時輸出的程式碼:

process.stdin.setEncoding("utf8");

process.stdin.on("readable",() => {
 let chunk;
 while ((chunk = process.stdin.read()) !== null) {
  process.stdout.write(`>>> ${chunk}`);
 }
});

process.stdin.on("end",() => {
 process.stdout.write("結束");
});

關於事件的含義,還是請看stream 的文件。

深入理解 process.nextTick

我第一次看到 process.nextTick 的時候是比較懵的,看文件可以知道,它的用途是:把回撥函式作為微任務,放入事件迴圈的任務佇列中。但這麼做的意義是什麼呢?

因為 nodejs 並不適合計算密集型的應用,一個程序就一個執行緒,在當下時間點上,就一個事件在執行。那麼,如果我們的事件佔用了很多 cpu 時間,那麼之後的事件就要等待非常久。所以,nodejs 的一個程式設計原則是儘量縮短每一個事件的執行事件。process.nextTick 的作用就在這,將一個大的任務分解成多個小的任務。示例程式碼如下:

// 被拆分成2個函式執行
function BigThing() {
 doPartThing();

 process.nextTick(() => finishThing());
}

在事件迴圈中,何時執行 nextTick 註冊的任務呢?請看下面的程式碼:

setTimeout(function() {
 console.log("第一個1秒");
 process.nextTick(function() {
  console.log("第一個1秒:nextTick");
 });
},1000);

setTimeout(function() {
 console.log("第2個1秒");
},1000);

console.log("我要輸出1");

process.nextTick(function() {
 console.log("nextTick");
});

console.log("我要輸出2");

輸出的結果如下,nextTick 是早於 setTimeout:

我要輸出1
我要輸出2
nextTick
第一個1秒
第一個1秒:nextTick
第2個1秒

在瀏覽器端,nextTick 會退化成 setTimeout(callback,0)。但在 nodejs 中請使用 nextTick 而不是 setTimeout,前者效率更高,並且嚴格來說,兩者建立的事件在任務佇列中順序並不一樣(請看前面的程式碼)。

以上就是詳解Node.JS模組 process的詳細內容,更多關於Node.JS模組 process的資料請關注我們其它相關文章!