首頁 >web前端 >js教程 >解析Node.js異常處理中domain模組的使用方法_node.js

解析Node.js異常處理中domain模組的使用方法_node.js

WBOY
WBOY原創
2016-05-16 15:15:222091瀏覽

NodeJS 提供了 domain 模組,可以簡化非同步程式碼的異常處理。在介紹這個模組之前,我們需要先理解「域」的概念。簡單的講,一個域就是一個 JS 運行環境,在一個運行環境中,如果一個異常沒有被捕獲,將作為一個全域異常被拋出。 NodeJS 透過 process 物件提供了捕捉全域異常的方法,範例程式碼如下

process.on('uncaughtException', function (err) {
  console.log('Error: %s', err.message);
});

setTimeout(function (fn) {
  fn();
});

Error: undefined is not a function

雖然全域異常有個地方可以捕獲了,但是對於大多數異常,我們希望儘早捕獲,並根據結果決定程式碼的執行路徑。我們用以下 HTTP 伺服器程式碼作為範例:

function async(request, callback) {
  // Do something.
  asyncA(request, function (err, data) {
    if (err) {
      callback(err);
    } else {
      // Do something
      asyncB(request, function (err, data) {
        if (err) {
          callback(err);
        } else {
          // Do something
          asyncC(request, function (err, data) {
            if (err) {
              callback(err);
            } else {
              // Do something
              callback(null, data);
            }
          });
        }
      });
    }
  });
}

http.createServer(function (request, response) {
  async(request, function (err, data) {
    if (err) {
      response.writeHead(500);
      response.end();
    } else {
      response.writeHead(200);
      response.end(data);
    }
  });
});

以上程式碼將請求物件交給非同步函數處理後,再根據處理結果回傳回應。這裡採用了使用回呼函數傳遞異常的方案,因此 async 函數內部如果再多幾個非同步函數呼叫的話,程式碼就變成上邊這副鬼樣子了。為了讓程式碼好好看點,我們可以在每處理一個請求時,使用 domain 模組建立一個子域(JS 子運行環境)。在子域內運行的程式碼可以隨意拋出異常,而這些異常可以透過子域物件的 error 事件統一捕獲。於是以上程式碼可以做以下改造:

function async(request, callback) {
  // Do something.
  asyncA(request, function (data) {
    // Do something
    asyncB(request, function (data) {
      // Do something
      asyncC(request, function (data) {
        // Do something
        callback(data);
      });
    });
  });
}

http.createServer(function (request, response) {
  var d = domain.create();

  d.on('error', function () {
    response.writeHead(500);
    response.end();
  });

  d.run(function () {
    async(request, function (data) {
      response.writeHead(200);
      response.end(data);
    });
  });
});

可以看到,我們使用.create方法建立了一個子域對象,並透過.run方法進入需要在子域中運行的程式碼的入口點。而位於子域中的非同步函數回呼函數由於不再需要捕獲異常,程式碼一下子瘦身很多。

陷阱
無論是透過process 物件的uncaughtException 事件擷取到全域異常,還是透過子網域物件的error 事件擷取到了子網域異常,在NodeJS 官方文件裡都強烈建議處理完異常後立即重啟程序,而不是讓程式繼續運行。根據官方文件的說法,發生異常後的程式處於不確定的運行狀態,如果不立即退出的話,程式可能會發生嚴重記憶體洩漏,也可能表現得很奇怪。

但這裡需要澄清一些事實。 JS 本身的throw..try..catch異常處理機制並不會導致記憶體洩漏,也不會讓程式的執行結果出乎意料,但 NodeJS 並不是存粹的 JS。 NodeJS 裡大量的API 內部是用C/C++ 實現的,因此NodeJS 程式的運行過程中,程式碼執行路徑穿梭於JS 引擎內部和外部,而JS 的異常拋出機制可能會打斷正常的程式碼執行流程,導致C/C++ 部分的程式碼表現異常,進而導致記憶體洩漏等問題。

因此,使用 uncaughtException 或 domain 捕獲異常,程式碼執行路徑裡涉及到了 C/C++ 部分的程式碼時,如果無法確定是否會導致記憶體洩漏等問題,最好在處理完異常後重啟程式比較妥當。而使用 try 語句捕捉異常時一般捕獲到的都是 JS 本身的異常,不用擔心上訴問題。

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn