鍍金池/ 教程/ 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生成的錯(cuò)誤分為兩類:JavaScript錯(cuò)誤和系統(tǒng)錯(cuò)誤。所有的錯(cuò)誤都繼承于JavaScriptError類,或就是它的實(shí)例。并且都至少提供這個(gè)類中可用的屬性。

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

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

JavaScript錯(cuò) 誤

JavaScript錯(cuò)誤表示 API 被錯(cuò)誤的使用了,或者正在寫的程序有問(wèn)題。

Class: Error

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

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

new Error(message)

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

error.message

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

error.stack

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

一個(gè)堆棧追蹤信息例子:

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)

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

幀只會(huì)由JavaScript函數(shù)生成。例如,如果在一個(gè)JavaScript函數(shù)里,同步執(zhí)行了一個(gè)叫cheetahify的 C++ addon函數(shù),那么堆棧追蹤信息中的幀里將不會(huì)有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

位置信息將會(huì)是以下之一:

  • 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í)生成,它是惰性生成的。

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

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

Error.captureStackTrace(targetObject[, constructorOpt])

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

var myObject = {};

Error.captureStackTrace(myObject);

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

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

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

這對(duì)于向最終用戶隱藏實(shí)現(xiàn)細(xì)節(jié)十分有用。一個(gè)普遍的使用這個(gè)參數(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

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

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

Class: RangeError

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

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

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

Class: TypeError

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

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

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

Class: ReferenceError

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

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

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

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

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

Class: SyntaxError

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

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

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

異常 vs. 錯(cuò)誤

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

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

系統(tǒng)錯(cuò)誤

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

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

Class: System Error

error.syscall

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

error.errno

error.code

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

常見系統(tǒng)錯(cuò)誤

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

EPERM: 操作不被允許

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

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

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

EACCES: 沒(méi)有權(quán)限

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

EEXIST: 文件已存在

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

ENOTDIR: 非目錄

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

EISDIR: 是目錄

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

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

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

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

EPIPE: 損壞的管道

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

EADDRINUSE: 地址已被使用

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

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

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

ECONNREFUSED: 拒絕連接

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

ENOTEMPTY: 目錄不為空

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

ETIMEDOUT: 操作超時(shí)

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

錯(cuò)誤的傳播和捕獲

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

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

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

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

單個(gè)操作使用Node風(fēng)格的回調(diào)函數(shù) -- 一個(gè)提供給 API 作為參數(shù)的函數(shù)。Node 風(fēng)格的回調(diào)函數(shù)至少有一個(gè)參數(shù) -- error -- 它可以是null(如果沒(méi)有錯(cuò)誤發(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 生成的錯(cuò)誤。一個(gè)初學(xué)者的常見錯(cuò)誤是嘗試在Node 風(fēng)格的回調(diào)函數(shù)中拋出錯(cuò)誤:

// 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
}

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

錯(cuò)誤事件

另一個(gè)提供錯(cuò)誤的機(jī)制是error事件。這常被用在基于流或基于event emitter的 API 中,它們自身就代表了一系列的異步操作(每一個(gè)單一的操作都可能成功或失敗)。如果在錯(cuò)誤的源頭沒(méi)有添加error事件的監(jiān)聽器,那么error會(huì)被拋出。此時(shí),進(jìn)程會(huì)因?yàn)橐粋€(gè)未處理的異常而掛掉,除非提供了合適的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)沒(méi)有沒(méi)有監(jiān)聽錯(cuò)誤時(shí)會(huì)拋出錯(cuò)誤”這個(gè)行為不僅限與io.js提供的API -- 用戶創(chuàng)建的基于流或event emitters的 API 也會(huì)如此。例子:

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)生的錯(cuò)誤也不能被try { } catch(err) { }捕獲 -- 它們發(fā)生時(shí),外圍的代碼已經(jīng)退出了。

上一篇:StringDecoder下一篇:REPL