io.js
生成的錯(cuò)誤分為兩類:JavaScript
錯(cuò)誤和系統(tǒng)錯(cuò)誤。所有的錯(cuò)誤都繼承于JavaScript
的Error
類,或就是它的實(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ò)誤表示API被錯(cuò)誤的使用了,或者正在寫的程序有問(wèn)題。
一個(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í)例。
實(shí)例化一個(gè)新的Error
對(duì)象,并且用提供的message
設(shè)置它的.message
屬性。它的.stack
屬性將會(huì)描述new Error
被調(diào)用時(shí)程序的這一刻。堆棧追蹤信息隸屬于V8堆棧追蹤API。堆棧追蹤信息只延伸到同步代碼執(zhí)行的開(kāi)始,或Error.stackTraceLimit
給出的幀數(shù)(number of frames),這取決于哪個(gè)更小。
一個(gè)在Error()
實(shí)例化時(shí)被傳遞的字符串。這個(gè)信息會(huì)出現(xiàn)在堆棧追蹤信息的第一行。改變這個(gè)值將不會(huì)改變堆棧追蹤信息的第一行。
這個(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. (/home/gbusey/actors.js:400:8)
at increaseSynergy (/home/gbusey/actors.js:701:6)
第一行被格式化為<錯(cuò)誤類名>: <錯(cuò)誤信息>
,然后是一系列的堆棧信息幀(以“at”
開(kāi)頭)。每幀都描述了一個(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í)例生成,參閱下文。
為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
一個(gè)決定了堆棧追蹤信息的堆棧幀數(shù)的屬性(不論是由new Error().stack
或由Error.captureStackTrace(obj)
生成)。
初始值是10
??梢员辉O(shè)置為任何有效的JavaScript
數(shù)字,當(dāng)值被改變后,就會(huì)影響所有的堆棧追蹤信息的獲取。如果設(shè)置為一個(gè)非數(shù)字值,堆棧追蹤將不會(huì)獲取任何一幀,并且會(huì)在要使用時(shí)報(bào)告undefined
。
一個(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)證的一種形式。
一個(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)證的一種形式。
一個(gè)Error
子類,表明了試圖去獲取一個(gè)未定義的對(duì)象的屬性。大多數(shù)情況下它表明了一個(gè)輸入錯(cuò)誤,或者一個(gè)不完整的程序??蛻舳舜a可能會(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。
一個(gè)Error
子類,表明了程序代碼不是合法的JavaScript
。這些錯(cuò)誤可能只會(huì)作為代碼運(yùn)行的結(jié)果生成。代碼運(yùn)行可能是eval
,Function
,require
或vm
的結(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ù)的 - 它們僅可能被其他上下文捕獲。
一個(gè)JavaScript
“異?!笔且粋€(gè)無(wú)效操作或throw
聲明所拋出的結(jié)果的值。但是這些值不被要求必須繼承于Error
。所有的由io.js
或JavaScript
運(yùn)行時(shí)拋出的異常都必須是Error
實(shí)例。
一些異常在JavaScript
層是無(wú)法恢復(fù)的。這些異常通常使一個(gè)進(jìn)程掛掉。它們通常無(wú)法通過(guò)assert()
檢查,或C++層中的abort()
調(diào)用。
系統(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 intro
或man 3 errno
在大多數(shù)Unices
中獲得;或在線獲得。
在io.js
中,系統(tǒng)錯(cuò)誤表現(xiàn)為一個(gè)增強(qiáng)的Error
對(duì)象 -- 不是完全的子類,而是一個(gè)有額外成員的error
實(shí)例。
一個(gè)代表了失敗的系統(tǒng)調(diào)用的字符串。
一個(gè)代表了錯(cuò)誤碼的字符串,通常是大寫字母E
,可在man 2 intro
命令的結(jié)果中查閱。
這個(gè)列表不詳盡,但是列舉了許多在寫io.js
的過(guò)程中普遍發(fā)生的系統(tǒng)錯(cuò)誤。詳盡的列表可以在這里查閱:http://man7.org/linux/man-pages/man3/errno.3.html
試圖去執(zhí)行一個(gè)需要特權(quán)的操作。
通常由文件操作產(chǎn)生;指定的路徑不存在 -- 通過(guò)指定的路徑不能找到實(shí)例(文件或目錄)。
試圖以禁止的方式去訪問(wèn)一個(gè)需要權(quán)限的文件。
執(zhí)行一個(gè)要求目標(biāo)不存在的操作時(shí),一個(gè)已存在文件已經(jīng)是目標(biāo)。
給定的路徑存在,但不是期望的目錄。通常由fs.readdir
產(chǎn)生。
一個(gè)操作期望接收一個(gè)文件,但給定的路徑是一個(gè)文件。
達(dá)到了系統(tǒng)中允許的文件描述符的最大數(shù)量,那么下一個(gè)描述符請(qǐng)求,在已存在的最后一個(gè)描述符關(guān)閉之前,都不能被滿足。
通常在并行打開(kāi)太多文件時(shí)觸發(fā),特別是在那些將進(jìn)程可用的文件描述符數(shù)量限制得很低的操作系統(tǒng)中(尤其是OS X)。為了改善這個(gè)限制,在同一個(gè)SHELL中運(yùn)行ulimit -n 2048
命令,再運(yùn)行io.js
進(jìn)程。
向沒(méi)有讀取數(shù)據(jù)進(jìn)程的管道,socket或FIFO中執(zhí)行一個(gè)寫操作。通常在網(wǎng)絡(luò)和http層發(fā)生,表明需要被寫入的遠(yuǎn)程流已經(jīng)被關(guān)閉。
試圖給一個(gè)服務(wù)器(net,http或https)綁定一個(gè)本地地址失敗,因?yàn)榱硪粋€(gè)本地系統(tǒng)中的服務(wù)器已經(jīng)使用了那個(gè)地址。
連接的雙方被強(qiáng)行關(guān)閉。通常是遠(yuǎn)程socket
超時(shí)或重啟的結(jié)果。通常由http
和net
模塊產(chǎn)生。
由于目標(biāo)機(jī)器積極拒絕,沒(méi)有連接可以建立。通常是試圖訪問(wèn)一個(gè)不活躍的遠(yuǎn)程主機(jī)的服務(wù)的結(jié)果。
操作的實(shí)例要求是一個(gè)空目錄,但目錄不為空 -- 通常由fs.unlink
產(chǎn)生。
因?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)志。
所有的io.js
API將無(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ò)誤事件)。
單個(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é)者的常見(jiàn)錯(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)聽(tīng)器,那么它也將會(huì)捕獲錯(cuò)誤。
另一個(gè)提供錯(cuò)誤的機(jī)制是error
事件。這常被用在基于流或基于event emitter
的API中,它們自身就代表了一系列的異步操作(每一個(gè)單一的操作都可能成功或失?。?。如果在錯(cuò)誤的源頭沒(méi)有添加error
事件的監(jiān)聽(tīng)器,那么error
會(huì)被拋出。此時(shí),進(jìn)程會(huì)因?yàn)橐粋€(gè)未處理的異常而掛掉,除非提供了合適的domains
,或監(jiān)聽(tīng)了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)聽(tīng)錯(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)退出了。
更多建議: