鍍金池/ 教程/ HTML/ Errors
punycode
Zlib
V8
Debugger
Readline
DNS
File System
util
Query String
Assert
執(zhí)行<code>JavaScript</code>
Console
Errors
Events
Timers
HTTP
Child Process
Buffer
Stream
Path
Modules
net
REPL
process
Global Objects
Crypto
StringDecoder
TTY
TLS (SSL)
OS
HTTPS
UDP / Datagram Sockets
Cluster
URL

Errors

io.js生成的錯誤分為兩類:JavaScript錯誤和系統(tǒng)錯誤。所有的錯誤都繼承于JavaScriptError類,或就是它的實(shí)例。并且都至少提供這個類中可用的屬性。

當(dāng)一個操作因?yàn)檎Z法錯誤或語言運(yùn)行時級別(language-runtime-level)的原因不被允許時,一個JavaScript error會被生成并拋出一個異常。如果一個操作因?yàn)橄到y(tǒng)級別(system-level)限制而不被允許時,一個系統(tǒng)錯誤會被生成??蛻舳舜a接著會根據(jù)API傳播它的方式來被給予捕獲這個錯誤的機(jī)會。

API被調(diào)用的風(fēng)格決定了生成的錯誤如何回送(handed back),傳播給客戶端。這反過來告訴客戶端如何捕獲它們。異??梢酝ㄟ^try / catch結(jié)構(gòu)捕獲;其他的捕獲方式請參閱下文。

JavaScript錯 誤

JavaScript錯誤表示 API 被錯誤的使用了,或者正在寫的程序有問題。

Class: Error

一個普通的錯誤對象。和其他的錯誤對象不同,Error實(shí)例不指示任何為什么錯誤發(fā)生的原因。Error在它們被實(shí)例化時,會記錄下“堆棧追蹤”信息,并且可以會提供一個錯誤描述。

注意:io.js會將系統(tǒng)錯誤以及JavaScript錯誤都封裝為這個類的實(shí)例。

new Error(message)

實(shí)例化一個新的Error對象,并且用提供的message設(shè)置它的.message屬性。它的.stack屬性將會描述new Error被調(diào)用時程序的這一刻。堆棧追蹤信息隸屬于 V8 堆棧追蹤 API。堆棧追蹤信息只延伸到同步代碼執(zhí)行的開始,或Error.stackTraceLimit給出的幀數(shù)(number of frames),這取決于哪個更小。

error.message

一個在Error()實(shí)例化時被傳遞的字符串。這個信息會出現(xiàn)在堆棧追蹤信息的第一行。改變這個值將不會改變堆棧追蹤信息的第一行。

error.stack

這個屬性返回一個代表錯誤被實(shí)例化時程序運(yùn)行的那個點(diǎn)的字符串。

一個堆棧追蹤信息例子:

Error: Things keep happening!
   at /home/gbusey/file.js:525:2
   at Frobnicator.refrobulate (/home/gbusey/business-logic.js:424:21)
   at Actor.<anonymous> (/home/gbusey/actors.js:400:8)
   at increaseSynergy (/home/gbusey/actors.js:701:6)

第一行被格式化為<錯誤類名>: <錯誤信息>,然后是一系列的堆棧信息幀(以“at”開頭)。每幀都描述了一個最終導(dǎo)致錯誤生成的一次調(diào)用的地點(diǎn)。V8 會試圖去給出每個函數(shù)的名字(通過變量名,函數(shù)名或?qū)ο蠓椒且灿锌赡芩也坏揭粋€合適的名字。如果 V8 不能為函數(shù)定義一個名字,那么那一幀里只會展示出位置信息。否則,被定義的函數(shù)名會顯示在位置信息之前。

幀只會由JavaScript函數(shù)生成。例如,如果在一個JavaScript函數(shù)里,同步執(zhí)行了一個叫cheetahify的 C++ addon函數(shù),那么堆棧追蹤信息中的幀里將不會有cheetahify調(diào)用:

var cheetahify = require('./native-binding.node');

function makeFaster() {
  // cheetahify *synchronously* calls speedy.
  cheetahify(function speedy() {
    throw new Error('oh no!');
  });
}

makeFaster(); // will throw:
// /home/gbusey/file.js:6
//     throw new Error('oh no!');
//           ^
// Error: oh no!
//     at speedy (/home/gbusey/file.js:6:11)
//     at makeFaster (/home/gbusey/file.js:5:3)
//     at Object.<anonymous> (/home/gbusey/file.js:10:1)
//     at Module._compile (module.js:456:26)
//     at Object.Module._extensions..js (module.js:474:10)
//     at Module.load (module.js:356:32)
//     at Function.Module._load (module.js:312:12)
//     at Function.Module.runMain (module.js:497:10)
//     at startup (node.js:119:16)
//     at node.js:906:3

位置信息將會是以下之一:

  • native,如果幀代表了向 V8 內(nèi)部的一次調(diào)用(如在[].forEach中)。
  • plain-filename.js:line:column,如果幀代表了向io.js內(nèi)部的一次調(diào)用。
  • /absolute/path/to/file.js:line:column,如果幀代表了向用戶程序或其依賴的一次調(diào)用。

關(guān)鍵的一點(diǎn)是,代表了堆棧信息的字符串只在需要被使用時生成,它是惰性生成的。

堆棧信息的幀數(shù)由 Error.stackTraceLimit 或 當(dāng)前事件循環(huán)的tick里可用的幀數(shù) 中小的一方?jīng)Q定。

系統(tǒng)級別錯誤被作為增強(qiáng)的Error實(shí)例生成,參閱下文。

Error.captureStackTrace(targetObject[, constructorOpt])

targetObject創(chuàng)建一個.stack屬性,它代表了Error.captureStackTrace被調(diào)用時,在程序中的位置。

var myObject = {};

Error.captureStackTrace(myObject);

myObject.stack  // similar to `new Error().stack`

追蹤信息的第一行,將是targetObject.toString()的結(jié)果,而不是一個帶有ErrorType:前綴的信息。

可選的constructorOpt接收一個函數(shù)。如果指定,所有constructorOpt以上的幀,包括constructorOpt,將會被生成的堆棧追蹤信息忽略。

這對于向最終用戶隱藏實(shí)現(xiàn)細(xì)節(jié)十分有用。一個普遍的使用這個參數(shù)的例子:

function MyError() {
  Error.captureStackTrace(this, MyError);
}

// without passing MyError to captureStackTrace, the MyError
// frame would should up in the .stack property. by passing
// the constructor, we omit that frame and all frames above it.

new MyError().stack

Error.stackTraceLimit

一個決定了堆棧追蹤信息的堆棧幀數(shù)的屬性(不論是由new Error().stack或由Error.captureStackTrace(obj)生成)。

初始值是10??梢员辉O(shè)置為任何有效的JavaScript數(shù)字,當(dāng)值被改變后,就會影響所有的堆棧追蹤信息的獲取。如果設(shè)置為一個非數(shù)字值,堆棧追蹤將不會獲取任何一幀,并且會在要使用時報(bào)告undefined

Class: RangeError

一個Error子類,表明了為一個函數(shù)提供的參數(shù)沒有在可接受的值的范圍之內(nèi);不論是在一個數(shù)字范圍之外,或是在一個參數(shù)指定的參數(shù)集合范圍之外。例子:

require('net').connect(-1);  // throws RangeError, port should be > 0 && < 65536

io.js會立刻生成并拋出一個RangeError實(shí)例 -- 它們是參數(shù)驗(yàn)證的一種形式。

Class: TypeError

一個Error子類,表明了提供的參數(shù)不是被允許的類型。例如,為一個期望收到字符串參數(shù)的函數(shù),傳入一個函數(shù)作為參數(shù),將導(dǎo)致一個類型錯誤。

require('url').parse(function() { }); // throws TypeError, since it expected a string

io.js會立刻生成并拋出一個TypeError實(shí)例 -- 它們是參數(shù)驗(yàn)證的一種形式。

Class: ReferenceError

一個Error子類,表明了試圖去獲取一個未定義的對象的屬性。大多數(shù)情況下它表明了一個輸入錯誤,或者一個不完整的程序。客戶端代碼可能會生成和傳播這些錯誤,但實(shí)際上只有 V8 會。

doesNotExist; // throws ReferenceError, doesNotExist is not a variable in this program.

ReferenceError實(shí)例將有一個.arguments屬性,它是一個包含了一個元素的數(shù)組。這個元素表示沒有被定義的那個變量。

try {
  doesNotExist;
} catch(err) {
  err.arguments[0] === 'doesNotExist';
}

除非用戶程序是動態(tài)生成并執(zhí)行的,否則,ReferenceErrors應(yīng)該永遠(yuǎn)被認(rèn)為是程序或其依賴模塊的bug。

Class: SyntaxError

一個Error子類,表明了程序代碼不是合法的JavaScript。這些錯誤可能只會作為代碼運(yùn)行的結(jié)果生成。代碼運(yùn)行可能是evalFunction,requirevm的結(jié)果。這些錯誤經(jīng)常表明了一個不完整的程序。

try {
  require("vm").runInThisContext("binary ! isNotOk");
} catch(err) {
  // err will be a SyntaxError
}

SyntaxError對于創(chuàng)建它們的上下文來說是不可恢復(fù)的 - 它們僅可能被其他上下文捕獲。

異常 vs. 錯誤

一個JavaScript“異?!笔且粋€無效操作或throw聲明所拋出的結(jié)果的值。但是這些值不被要求必須繼承于Error。所有的由io.jsJavaScript運(yùn)行時拋出的異常都必須是Error實(shí)例。

一些異常在JavaScript層是無法恢復(fù)的。這些異常通常使一個進(jìn)程掛掉。它們通常無法通過assert()檢查,或 C++ 層中的abort()調(diào)用。

系統(tǒng)錯誤

系統(tǒng)錯誤在程序運(yùn)行時環(huán)境的響應(yīng)中生成。理想情況下,它們代表了程序能夠處理的操作錯誤。它們在系統(tǒng)調(diào)用級別生成:一個詳盡的錯誤碼列表和它們意義可以通過運(yùn)行man 2 introman 3 errno在大多數(shù)Unices中獲得;或在線獲得。

io.js中,系統(tǒng)錯誤表現(xiàn)為一個增強(qiáng)的Error對象 -- 不是完全的子類,而是一個有額外成員的error實(shí)例。

Class: System Error

error.syscall

一個代表了失敗的系統(tǒng)調(diào)用的字符串。

error.errno

error.code

一個代表了錯誤碼的字符串,通常是大寫字母E,可在man 2 intro命令的結(jié)果中查閱。

常見系統(tǒng)錯誤

這個列表不詳盡,但是列舉了許多在寫io.js的過程中普遍發(fā)生的系統(tǒng)錯誤。詳盡的列表可以在這里查閱:http://man7.org/linux/man-pages/man3/errno.3.html

EPERM: 操作不被允許

試圖去執(zhí)行一個需要特權(quán)的操作。

ENOENT: 指定的文件或目錄不存在

通常由文件操作產(chǎn)生;指定的路徑不存在 -- 通過指定的路徑不能找到實(shí)例(文件或目錄)。

EACCES: 沒有權(quán)限

試圖以禁止的方式去訪問一個需要權(quán)限的文件。

EEXIST: 文件已存在

執(zhí)行一個要求目標(biāo)不存在的操作時,一個已存在文件已經(jīng)是目標(biāo)。

ENOTDIR: 非目錄

給定的路徑存在,但不是期望的目錄。通常由fs.readdir產(chǎn)生。

EISDIR: 是目錄

一個操作期望接收一個文件,但給定的路徑是一個文件。

EMFILE: 系統(tǒng)中打開太多文件

達(dá)到了系統(tǒng)中允許的文件描述符的最大數(shù)量,那么下一個描述符請求,在已存在的最后一個描述符關(guān)閉之前,都不能被滿足。

通常在并行打開太多文件時觸發(fā),特別是在那些將進(jìn)程可用的文件描述符數(shù)量限制得很低的操作系統(tǒng)中(尤其是 OS X)。為了改善這個限制,在同一個 SHELL 中運(yùn)行ulimit -n 2048命令,再運(yùn)行io.js進(jìn)程。

EPIPE: 損壞的管道

向沒有讀取數(shù)據(jù)進(jìn)程的管道,socket 或 FIFO 中執(zhí)行一個寫操作。通常在網(wǎng)絡(luò)和 http 層發(fā)生,表明需要被寫入的遠(yuǎn)程流已經(jīng)被關(guān)閉。

EADDRINUSE: 地址已被使用

試圖給一個服務(wù)器(net,http 或 https)綁定一個本地地址失敗,因?yàn)榱硪粋€本地系統(tǒng)中的服務(wù)器已經(jīng)使用了那個地址。

ECONNRESET: 連接兩方重置(Connection reset by peer)

連接的雙方被強(qiáng)行關(guān)閉。通常是遠(yuǎn)程socket超時或重啟的結(jié)果。通常由httpnet模塊產(chǎn)生。

ECONNREFUSED: 拒絕連接

由于目標(biāo)機(jī)器積極拒絕,沒有連接可以建立。通常是試圖訪問一個不活躍的遠(yuǎn)程主機(jī)的服務(wù)的結(jié)果。

ENOTEMPTY: 目錄不為空

操作的實(shí)例要求是一個空目錄,但目錄不為空 -- 通常由fs.unlink產(chǎn)生。

ETIMEDOUT: 操作超時

因?yàn)楸贿B接方在一段指定內(nèi)未響應(yīng),連接或發(fā)送請求失敗。通常由 http 或 net 產(chǎn)生 -- 經(jīng)常是一個 被連接socket沒有合適地調(diào)用.end()方法 的標(biāo)志。

錯誤的傳播和捕獲

所有的io.jsAPI 將無效的參數(shù)視作異常 -- 也就是說,如果傳遞了非法的參數(shù),他們會立刻生成并拋出一個error作為異常,甚至是異步 API 也會。

同步 API(像fs.readFileSync)將會拋出一個錯誤。拋出值的行為是將值包裝入一個異常。異??梢员皇褂?code>try { } catch(err) { }結(jié)果捕獲。

異步 API 有兩種錯誤傳播機(jī)制;一種代表了單個操作(Node 風(fēng)格的回調(diào)函數(shù)),另一種代表了多個操作(錯誤事件)。

Node 風(fēng)格的回調(diào)函數(shù)

單個操作使用Node風(fēng)格的回調(diào)函數(shù) -- 一個提供給 API 作為參數(shù)的函數(shù)。Node 風(fēng)格的回調(diào)函數(shù)至少有一個參數(shù) -- error -- 它可以是null(如果沒有錯誤發(fā)生)或是Error實(shí)例。例子:

var fs = require('fs');

fs.readFile('/some/file/that/does-not-exist', function nodeStyleCallback(err, data) {
  console.log(err)  // Error: ENOENT
  console.log(data) // undefined / null
});

fs.readFile('/some/file/that/does-exist', function(err, data) {
  console.log(err)  // null
  console.log(data) // <Buffer: ba dd ca fe>
})

注意,try { } catch(err) { }不能捕獲異步 API 生成的錯誤。一個初學(xué)者的常見錯誤是嘗試在Node 風(fēng)格的回調(diào)函數(shù)中拋出錯誤:

// THIS WILL NOT WORK:
var fs = require('fs');

try {
  fs.readFile('/some/file/that/does-not-exist', function(err, data) {
    // mistaken assumption: throwing here...
    if (err) {
      throw err;
    }
  });
} catch(err) {
  // ... will be caught here -- this is incorrect!
  console.log(err); // Error: ENOENT
}

這將不會正常運(yùn)行!在 Node 風(fēng)格的回調(diào)函數(shù)執(zhí)行時,外圍的代碼try { } catch(err) { })已經(jīng)退出了。在大多數(shù)情況,在 Node 風(fēng)格的回調(diào)函數(shù)內(nèi)部拋出錯誤會使進(jìn)程掛掉。如果啟用了domain,它們可以捕獲了被拋出的錯誤;相似的,如果給process.on('uncaughtException')添加了監(jiān)聽器,那么它也將會捕獲錯誤。

錯誤事件

另一個提供錯誤的機(jī)制是error事件。這常被用在基于流或基于event emitter的 API 中,它們自身就代表了一系列的異步操作(每一個單一的操作都可能成功或失敗)。如果在錯誤的源頭沒有添加error事件的監(jiān)聽器,那么error會被拋出。此時,進(jìn)程會因?yàn)橐粋€未處理的異常而掛掉,除非提供了合適的domains,或監(jiān)聽了process.on('uncaughtException')。

var net = require('net');

var connection = net.connect('localhost');

// adding an "error" event handler to a stream:
connection.on('error', function(err) {
  // if the connection is reset by the server, or if it can't
  // connect at all, or on any sort of error encountered by
  // the connection, the error will be sent here.
  console.error(err);
});

connection.pipe(process.stdout);

“當(dāng)沒有沒有監(jiān)聽錯誤時會拋出錯誤”這個行為不僅限與io.js提供的API -- 用戶創(chuàng)建的基于流或event emitters的 API 也會如此。例子:

var events = require('events');

var ee = new events.EventEmitter;

setImmediate(function() {
  // this will crash the process because no "error" event
  // handler has been added.
  ee.emit('error', new Error('This will crash'));
});

與 Node 風(fēng)格的回調(diào)函數(shù)相同,這種方式產(chǎn)生的錯誤也不能被try { } catch(err) { }捕獲 -- 它們發(fā)生時,外圍的代碼已經(jīng)退出了。

上一篇:StringDecoder下一篇:REPL