NodeJS之異常處理

龍恩0707發表於2019-03-25

1. 為什麼要處理異常?

如果我們不處理異常的話,直接會導致程式奔潰,使用者體驗比較差,因此我們要對異常進行處理,當出現異常的情況下,我們要給使用者一個友好的提示,並且記錄該異常,方便我們排查。

2. 在Node.js中常用的異常處理方式有哪些?

2.1 使用try catch方式來處理異常,如下程式碼:

try {
  throw new Error('error');
} catch(e) {
  console.log('異常被捕獲了,我現在還可以繼續執行了');
  console.log(e);
}

然後執行命令列時候,可以看到如下,也會列印後面的 console.log的資訊了;如下所示:

但是使用 try catch 無法處理非同步程式碼塊內出現的異常,比如如下程式碼:

try {
  setTimeout(() => {
    throw new Error('error');
  })
} catch(e) {
  console.log('異常被捕獲了,我現在還可以繼續執行了');
  console.log(e);
}

在命令列中列印資訊如下:

可以看到,catch裡面的console.log(''); 程式碼並沒有被執行。說明catch裡面的程式碼異常並沒有被捕獲到。

2.2 使用event方式來處理異常

const events = require('events');

// 建立一個事件監聽物件
const emitter = new events.EventEmitter();

// 監聽error事件
emitter.addListener('error', (e) => {
  // 處理異常資訊
  console.log(11122222); // 能列印 1112222 說明異常捕獲到了
  console.log(e);
});

// 觸發 error事件
emitter.emit('error', new Error('你程式碼出錯了'));

執行效果如下圖所示:

2.3 callback的方式

比如讀取一個檔案,或者建立一個目錄,測試程式碼如下:

const fs = require('fs');

fs.mkdir('/dir', (e) => {
  if (e) {
    console.log('異常資訊處理');
    console.log(e);
  } else {
    console.log('建立目錄成功');
  }
});

然後執行結果如下圖所示:

2.4 Promise方式

new Promise((resolve, reject) => {
  throw new Error('error');
}).then(() => {
  // 。。。。
}).catch((e) => {
  console.log('能進來說明可以處理異常資訊了');
  console.log(e);
});

執行結果如下圖所示:

如上是處理同步程式碼,但是如果是非同步程式碼呢?繼續如下程式碼測試:

new Promise((resolve, reject) => {
  setTimeout(() => {
    throw new Error('error');
  }, 100);
}).then(() => {
  // 。。。。
}).catch((e) => {
  console.log('能進來說明可以處理異常資訊了');
  console.log(e);
});

然後執行結果如下所示:

可以看到,Promise也是一樣無法捕獲非同步程式碼中的異常資訊了。

2.5 Async/Await 方式

Async/Await 也是基於Promise的,Promise是無法捕獲非同步異常,因此Async/Await 也是沒有辦法捕獲的。如下測試程式碼:

先看同步程式碼可以捕獲到的,程式碼如下:

const testFunc = function() {
  return new Promise((resolve, reject) => {
    throw new Error('error');
  });
};

async function testAsync() {
  try {
    await testFunc();
  } catch (e) {
    console.log('能進來,說明異常能處理');
    console.log(e);
  }
}

testAsync();

執行結果如下所示:

我們再看非同步程式碼,如下所示:

const testFunc = function() {
  setTimeout(() => {
    console.log(1111);
    return new Promise((resolve, reject) => {
      throw new Error('error');
    });
  }, 100);
};

async function testAsync() {
  try {
    await testFunc();
  } catch (e) {
    console.log('能進來,說明異常能處理');
    console.log(e);
  }
}

testAsync();

如下圖所示:

2.6 process 方式(該方式既可以處理同步程式碼的異常,也可以處理非同步程式碼的異常)。

如下同步程式碼異常:

process.on('uncaughtException', (e) => {
  console.log('我能進來,說明可以處理異常');
  console.log(e);
});

function testFunc() {
  throw new Error('error');
}

testFunc();

執行結果如下所示:

非同步程式碼如下所示:

process.on('uncaughtException', (e) => {
  console.log('我能進來,說明可以處理異常');
  console.log(e);
});

function testFunc() {
  setTimeout(() => {
    throw new Error('error');
  }, 100);
}

testFunc();

如下圖所示:

2.7 domain 方式

domain也可以處理任何型別異常的資訊,包含同步和非同步。

如下同步程式碼所示:

const domain = require('domain');
const d = domain.create();

d.on('error', (e) => {
  console.log('我能進來,說明能處理異常');
  console.log(e);
});

d.run(() => {
  throw new Error('同步程式碼處理');
});

如下圖所示:

非同步程式碼如下所示:

const domain = require('domain');
const d = domain.create();

d.on('error', (e) => {
  console.log('我能進來,說明能處理異常');
  console.log(e);
});

d.run(() => {
  setTimeout(() => {
    throw new Error('非同步程式碼處理');
  }, 100);
});

如下圖所示: