编程中有什么谬误?
在咱们的程序中,事件并非一帆风顺。
特地是在某些状况下,咱们可能心愿在 进行程序或在产生不良情况时告诉用户 。
例如:
- 程序试图关上一个不存在的文件。
- 网络连接断开。
- 用户进行了有效的输出。
在所有的这些状况下,咱们作为程序员都会产生 谬误,或者让编程引擎为咱们创立一些谬误。
在创立谬误之后,咱们能够向用户告诉音讯,或者能够齐全进行执行。
JavaScript 中有什么谬误?
JavaScript 中的 谬误是一个对象,随后被抛出,用以终止程序。
要在 JavaScript 中创立新谬误,咱们调用相应的 构造函数。例如,要创立一个新的通用谬误,能够执行以下操作:
const err = new Error("Something bad happened!");
创立谬误对象时,也能够省略关键字 new
:
const err = Error("Something bad happened!");
创立后,谬误对象将显示三个属性:
message
:带有错误信息的字符串。name
:谬误的类型。stack
:函数执行的栈跟踪。
例如,如果咱们用适当的音讯创立一个新的 TypeError
对象,则 message
将携带理论的谬误字符串,而 name
则为 TypeError
:
const wrongType = TypeError("Wrong type given, expected number");
wrongType.message; // "Wrong type given, expected number"
wrongType.name; // "TypeError"
Firefox 还实现了一堆非标准属性,例如 columnNumber
,filename
和 lineNumber
。
JavaScript 中的谬误类型
JavaScript 中有很多类型的谬误,即:
Error
EvalError
InternalError
RangeError
ReferenceError
SyntaxError
TypeError
URIError
请记住,所有这些谬误类型都是 理论构造函数,旨在返回一个新的谬误对象。
在代码中次要用 Error
和 TypeError
这两种最常见的类型来创立本人的谬误对象。
然而在大多数状况下,很多谬误间接来自 JavaScript 引擎,例如 InternalError
或 SyntaxError
。
上面的例子是当你尝试从新为 const
赋值时,将触发 TypeError
:
const name = "Jules";
name = "Caty";
// TypeError: Assignment to constant variable.
当你关键字拼错时,就会触发 SyntaxError
:
va x = '33';
// SyntaxError: Unexpected identifier
或者,当你在谬误的中央应用保留关键字时,例如在 async
函数之外的应用 await
:
function wrong(){await 99;}
wrong();
// SyntaxError: await is only valid in async function
当在页面中抉择不存在的 HTML 元素时,会产生 TypeError
:
Uncaught TypeError: button is null
除了这些“传统的”谬误对象外,AggregateError
对象也行将可能在 JavaScript 中应用。
AggregateError
能够把多个谬误很不便地包装在一起,在前面将会看到。
除了这些内置谬误外,在浏览器中还能够找到:
DOMException
DOMError
已弃用,目前不再应用。
DOMException
是与 Web API 相干的一系列谬误。无关残缺列表,请参见 MDN。
什么是异样?
很多人认为谬误和异样是一回事。实际上 谬误对象仅在抛出时才成为异样。
要在 JavaScript 中引发异样,咱们应用 throw
关键字,前面跟谬误对象:
const wrongType = TypeError("Wrong type given, expected number");
throw wrongType;
更常见的是缩写模式,在大多数代码库中,你都能够找到:
throw TypeError("Wrong type given, expected number");
或者:
throw new TypeError("Wrong type given, expected number");
个别不会把异样抛出到函数或条件块之外,当然也有例外情况,例如:
function toUppercase(string) {if (typeof string !== "string") {throw TypeError("Wrong type given, expected a string");
}
return string.toUpperCase();}
在代码中咱们查看函数的参数是否为字符串,如果不是则抛出异样。
从技术上讲,你能够在 JavaScript 中抛出任何货色,而不仅仅是谬误对象:
throw Symbol();
throw 33;
throw "Error!";
throw null;
然而,最好不要这样做,应该 总是抛出正确的谬误对象,而不是原始类型。
这样就能够通过代码库放弃错误处理的一致性。其余团队成员总是可能在谬误对象上拜访 error.message
或 error.stack
。
当抛出异样时会产生什么?
异样就像电梯在回升 :一旦抛出一个异样, 它就会在程序栈中冒泡,除非被卡在某个中央。
看上面的代码:
function toUppercase(string) {if (typeof string !== "string") {throw TypeError("Wrong type given, expected a string");
}
return string.toUpperCase();}
toUppercase(4);
如果你在浏览器或 Node.js 中运行这段代码,程序将进行并报告谬误:
Uncaught TypeError: Wrong type given, expected a string
toUppercase http://localhost:5000/index.js:3
<anonymous> http://localhost:5000/index.js:9
另外还能够看到产生谬误的代码行数。
这段报告是 栈跟踪(stack trace),对于跟踪代码中的问题很有帮忙。
栈跟踪从底部到顶部。所以是这样的:
toUppercase http://localhost:5000/index.js:3
<anonymous> http://localhost:5000/index.js:9
咱们能够说:
- 程序的第 9 行中名为
toUppercase
的内容 toUppercase
在第 3 行引发了一个问题
除了在浏览器的控制台中看到栈跟踪之外,还能够在谬误对象的 stack
属性上对其进行拜访。
如果异样是未捕捉的,也就是说程序员没有采取任何措施来捕捉它,则程序将会解体。
你在什么时候及在什么中央捕捉代码中的异样取决于特定的用例。
例如,你可能想要在栈中流传异样,使程序齐全解体。当产生致命的谬误,须要更平安地进行程序而不是解决有效数据时,你可能须要这样做。
介绍了基础知识之后,当初让咱们将注意力转向 同步和异步 JavaScript 代码中的谬误和异样解决。
同步错误处理
同步代码通常很简略,它的错误处理也是如此。
惯例函数的错误处理
同步代码依照代码程序循序渐进的执行。让咱们再来看后面的例子:
function toUppercase(string) {if (typeof string !== "string") {throw TypeError("Wrong type given, expected a string");
}
return string.toUpperCase();}
toUppercase(4);
在这里,引擎调用并执行 toUppercase
。所有操作都 同步 进行。要 捕捉 由这种同步函数产生的异样,能够用 try/catch/finally
:
try {toUppercase(4);
} catch (error) {console.error(error.message);
// or log remotely
} finally {// clean up}
通常try
解决主解决流程或者可能引发异样的函数调用。
而 catch
则捕捉理论的异样。它 接管谬误对象,能够在这里对其进行查看(并近程发送到生产环境中的日志服务器)。
另外无论函数的执行后果如何,不论是胜利还是失败,finally
中的所有代码都会被执行。
请记住:try/catch/finally
是一个 同步 构造:它能够捕捉来自异步代码的异样。
生成器函数的错误处理
JavaScript 中的生成器函数是一种非凡的函数。
除了在其外部作用域和使用者之间提供 双向通信通道 之外,它还能够 随便暂停和复原。
要创立一个生成器函数,须要在关键字 function
之后加一个星号 *
:
function* generate() {//}
进入函数后,能够应用 yield
返回值:
function* generate() {
yield 33;
yield 99;
}
生成器函数的返回值是 迭代器对象 。有两种办法 从生成器中提取值:
- 在迭代器对象上调用
next()
。 - iteration with
for...of
. - 带有
for ... of
的迭代。
以下面的代码为例,要从生成器获取值,能够这样做:
function* generate() {
yield 33;
yield 99;
}
const go = generate();
当调用生成器函数时,go
成了咱们的迭代器对象。
当初能够调用go.nex()
来执行:
function* generate() {
yield 33;
yield 99;
}
const go = generate();
const firstStep = go.next().value; // 33
const secondStep = go.next().value; // 99
生成器也能够通过其余形式工作:它们能够承受调用者返回的值和异样。
除了 next()
外,从生成器返回的迭代器对象还有 throw()
办法。用这个办法,能够通过把异样注入到生成器来暂停程序:
function* generate() {
yield 33;
yield 99;
}
const go = generate();
const firstStep = go.next().value; // 33
go.throw(Error("Tired of iterating!"));
const secondStep = go.next().value; // never reached
能够用 try/catch
(和 finally
,如果需要的话)将代码包装在生成器中来捕捉这样的谬误:
function* generate() {
try {
yield 33;
yield 99;
} catch (error) {console.error(error.message);
}
}
生成器函数还能够向内部抛出异样。捕捉这些异样的机制与捕捉同步异样的机制雷同:try/catch/finally
。
上面是通过 for ... of
从内部应用的生成器函数的例子:
function* generate() {
yield 33;
yield 99;
throw Error("Tired of iterating!");
}
try {for (const value of generate()) {console.log(value);
}
} catch (error) {console.error(error.message);
}
/* Output:
33
99
Tired of iterating!
*/
代码中迭代 try
块内的主解决流程。如果产生任何异样,就用 catch
进行。
异步错误处理
JavaScript 在实质上是同步的,是一种单线程语言。
诸如浏览器引擎之类的环境用许多 Web API 加强了 JavaScript,用来与内部零碎进行交互并解决与 I/O 绑定的操作。
浏览器中的异步示例包含timeouts、events、Promise。
异步代码中的错误处理与同步代码不同。
看一些例子:
计时器错误处理
在你开始学习 JavaScript 时,当学 try/catch/finally
之后,你可能会想把它们放在任何代码块中。
思考上面的代码段:
function failAfterOneSecond() {setTimeout(() => {throw Error("Something went wrong!");
}, 1000);
}
这个函数将在大概 1 秒钟后被触发。那么解决这个异样的正确形式是什么?
上面的例子是 有效的:
function failAfterOneSecond() {setTimeout(() => {throw Error("Something went wrong!");
}, 1000);
}
try {failAfterOneSecond();
} catch (error) {console.error(error.message);
}
正如后面所说的,try/catch
是同步的。另一方面,咱们有 setTimeout
,这是一个用于定时器的浏览器 API。
到传递给 setTimeout
的回调运行时,try/catch
曾经“隐没了”。程序将会解体,因为咱们无奈捕捉异样。
它们在两条不同的轨道上行驶:
Track A: --> try/catch
Track B: --> setTimeout --> callback --> throw
如果咱们不想使程序解体,为了正确处理错误,咱们必须把 try/catch
挪动到 setTimeout
的回调中。
然而这在大多数状况下并没有什么意义。Promises 的异步错误处理提供了更好的形式。
事件的错误处理
文档对象模型中的 HTML 节点连贯到 EventTarget
,EventTarget
是浏览器中所有 event emitter 的独特先人。
这意味着咱们能够侦听页面中任何 HTML 元素上的事件。Node.js 将在将来版本中反对 EventTarget
。
DOM 事件的错误处理机制遵循与异步 Web API 的雷同计划。
看上面的例子:
const button = document.querySelector("button");
button.addEventListener("click", function() {throw Error("Can't touch this button!");
});
在这里,单击按钮后会立刻引发异样,应该怎么捕捉它?上面的办法 不起作用,而且不会阻止程序解体:
const button = document.querySelector("button");
try {button.addEventListener("click", function() {throw Error("Can't touch this button!");
});
} catch (error) {console.error(error.message);
}
与后面的带有 setTimeout
的例子一样,传递给 addEventListener
的任何回调均 异步 执行:
Track A: --> try/catch
Track B: --> addEventListener --> callback --> throw
如果不想使程序解体,为了正确处理错误,必须把 try/catch
放在 addEventListener
的回调内。但这样做没有任何价值。与 setTimeout
一样,异步代码门路引发的异样从内部是 无奈捕捉 的,这将会使程序解体。
How about onerror?
怎么解决 onerror?
HTML 元素具备许多事件处理函数,例如 onclick
、onmouseenter
和 onchange
等。
还有 onerror
,然而它与 throw
没有什么关系。
每当像 <img>
标签或 <script>
之类的 HTML 元素遇到不存在的资源时,onerror
事件处理函数都会触发。
看上面的例子:
// omitted
<body>
<img src="nowhere-to-be-found.png" alt="So empty!">
</body>
// omitted
当拜访短少或不存在资源的 HTML 文档时,浏览器的控制台会输入以下谬误:
GET http://localhost:5000/nowhere-to-be-found.png
[HTTP/1.1 404 Not Found 3ms]
在 JavaScript 中,咱们有机会应用适当的事件处理程序来“捕捉”这个谬误:
const image = document.querySelector("img");
image.onerror = function(event) {console.log(event);
};
或者用更好的办法:
const image = document.querySelector("img");
image.addEventListener("error", function(event) {console.log(event);
});
此模式可用于 加载代替资源来替换失落的图像或脚本。
然而要记住:onerror
与 throw
或 try/catch
无关。
用 Promise 处理错误
为了阐明 Promise 的错误处理,咱们将“Promise”后面的一个例子。调整以下性能:
function toUppercase(string) {if (typeof string !== "string") {throw TypeError("Wrong type given, expected a string");
}
return string.toUpperCase();}
toUppercase(4);
为了代替返回简略的字符串或异样,能够别离用 Promise.reject
和 Promise.resolve
处理错误和胜利:
function toUppercase(string) {if (typeof string !== "string") {return Promise.reject(TypeError("Wrong type given, expected a string"));
}
const result = string.toUpperCase();
return Promise.resolve(result);
}
从技术上讲,这段代码中没有异步的货色,然而它能很好地阐明这一点。
当初函数已“promise 化”,咱们能够通过 then
应用后果,并附加 catch
来 解决被回绝的 Promise:
toUppercase(99)
.then(result => result)
.catch(error => console.error(error.message));
这段代码将会输入:
Wrong type given, expected a string
在 Promise 畛域,catch
是用于处理错误的构造。
除了 catch
和 then
之外,还有 finally
,相似于 try/catch
中的 finally
。
绝对于同步而言,Promise 的 finally
运行与 Promise 后果 无关:
toUppercase(99)
.then(result => result)
.catch(error => console.error(error.message))
.finally(() => console.log("Run baby, run"));
切记,传递给 then/catch/finally
的任何回调都是由微工作队列异步解决的。微工作优先于宏工作,例如事件和计时器。
Promise, error 和 throw
作为 回绝 Promise 的最佳办法,提供谬误对象很不便:
Promise.reject(TypeError("Wrong type given, expected a string"));
这样,你能够通过代码库放弃错误处理的一致性。其余团队成员总是能够冀望拜访 error.message
,更重要的是你能够查看栈跟踪。
除了 Promise.reject
之外,能够通过抛出异样来退出 Promise 链。
看上面的例子:
Promise.resolve("A string").then(value => {if (typeof value === "string") {throw TypeError("Expected a number!");
}
});
咱们用一个字符串解决一个 Promise,而后立刻用 throw
突破这个链。
为了阻止异样的流传,照常应用 catch
:
Promise.resolve("A string")
.then(value => {if (typeof value === "string") {throw TypeError("Expected a number!");
}
})
.catch(reason => console.log(reason.message));
这种模式在 fetch
中很常见,咱们在其中查看响应对象并查找谬误:
fetch("https://example-dev/api/")
.then(response => {if (!response.ok) {throw Error(response.statusText);
}
return response.json();})
.then(json => console.log(json));
在这里能够用 catch
拦挡异样。如果失败了,或者决定不去捕捉它,则 异样能够在栈中冒泡。
从实质上讲,这还不错,然而在不同的环境下对未捕捉的 rejection 的反馈不同。
例如,未来的 Node.js 将使任何未解决 Promise rejection 的程序解体:
DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
更好地捕捉他们!
错误处理“promisified”计时器
应用计时器或事件无奈捕捉从回调引发的异样。咱们在上一节中看到了例子:
function failAfterOneSecond() {setTimeout(() => {throw Error("Something went wrong!");
}, 1000);
}
// DOES NOT WORK
try {failAfterOneSecond();
} catch (error) {console.error(error.message);
}
Promise 提供的解决方案在于代码的“promisification”。基本上,咱们用 Promise 包装计时器:
function failAfterOneSecond() {return new Promise((_, reject) => {setTimeout(() => {reject(Error("Something went wrong!"));
}, 1000);
});
}
通过 reject
,咱们启动了 Promise rejection,它带有一个谬误对象。
这时能够用 catch
解决异样:
failAfterOneSecond().catch(reason => console.error(reason.message));
留神:通常应用 value
作为 Promise 的返回值,并用 reason
作为 rejection 的返回对象。
Node.js 有一个名为 promisify 的工具函数,能够简化新式回调 API 的“混淆”。
Promise.all 中的错误处理
静态方法 Promise.all
承受一个 Promise 数组,并返回所有解析 Promise 的后果数组:
const promise1 = Promise.resolve("All good!");
const promise2 = Promise.resolve("All good here too!");
Promise.all([promise1, promise2]).then((results) => console.log(results));
// ['All good!', 'All good here too!']
如果这些 Promise 中的任何一个被回绝,Promise.all
都会回绝,并返回第一个被回绝的 Promise 中的谬误。
为了在 Promise.all
中解决这些状况,须要应用 catch
,就像在上一节中所做的那样:
const promise1 = Promise.resolve("All good!");
const promise2 = Promise.reject(Error("No good, sorry!"));
const promise3 = Promise.reject(Error("Bad day ..."));
Promise.all([promise1, promise2, promise3])
.then(results => console.log(results))
.catch(error => console.error(error.message));
要再次运行函数而不思考 Promise.all
的后果,咱们能够应用 finally
。
Promise.all([promise1, promise2, promise3])
.then(results => console.log(results))
.catch(error => console.error(error.message))
.finally(() => console.log("Always runs!"));
Promise.any 中的错误处理
咱们能够将 Promise.any
(Firefox> 79,Chrome> 85)视为与 Promise.all
相同。
即便数组中的一个 Promise 回绝,Promise.all
也会返回失败,而 Promise.any
总是提供第一个已解决的 Promise(如果存在于数组中),无论产生了什么回绝。
如果传递给 Promise.any
的 Promise 不是都被回绝,则产生的谬误是 AggregateError
。思考以下示例:
const promise1 = Promise.reject(Error("No good, sorry!"));
const promise2 = Promise.reject(Error("Bad day ..."));
Promise.any([promise1, promise2])
.then(result => console.log(result))
.catch(error => console.error(error))
.finally(() => console.log("Always runs!"));
这里用 catch
处理错误。这里代码的输入是:
AggregateError: No Promise in Promise.any was resolved
Always runs!
AggregateError
对象有与根本 Error
雷同的属性,以及 Errors
属性:
//
.catch(error => console.error(error.errors))
//
这个属性是回绝产生的每个谬误的数组:
[Error: "No good, sorry!, Error:"Bad day ..."]
Promise.race 中的错误处理
静态方法 Promise.race
承受一个 Promise 数组:
const promise1 = Promise.resolve("The first!");
const promise2 = Promise.resolve("The second!");
Promise.race([promise1, promise2]).then(result => console.log(result));
// The first!
后果是 第一个博得“race”的 Promise。
那 rejection 呢?如果回绝的 Promise 不是第一个呈现在输出数组中的对象,则 Promise.race
解析:
const promise1 = Promise.resolve("The first!");
const rejection = Promise.reject(Error("Ouch!"));
const promise2 = Promise.resolve("The second!");
Promise.race([promise1, rejection, promise2]).then(result =>
console.log(result)
);
// The first!
如果 rejection 呈现在数组的第一个元素中,则 Promise.race
被回绝,咱们必须捕捉它:
const promise1 = Promise.resolve("The first!");
const rejection = Promise.reject(Error("Ouch!"));
const promise2 = Promise.resolve("The second!");
Promise.race([rejection, promise1, promise2])
.then(result => console.log(result))
.catch(error => console.error(error.message));
// Ouch!
Promise.allSettled 中的错误处理
Promise.allSettled
是对该语言的 ECMAScript 2020 补充。
这个静态方法没有什么要解决的,因为 即便一个或多个输出 Promise 被回绝,后果也始终是一个已解决的 Promise 。
看上面的例子:
const promise1 = Promise.resolve("Good!");
const promise2 = Promise.reject(Error("No good, sorry!"));
Promise.allSettled([promise1, promise2])
.then(results => console.log(results))
.catch(error => console.error(error))
.finally(() => console.log("Always runs!"));
咱们将由两个 Promise 组成的数组传递给 Promise.allSettled
:一个已解决,另一个被回绝。
在这种状况下,catch
将永远不会被执行。finally
会运行。
日志输入的 then
的代码的后果是:
[{ status: 'fulfilled', value: 'Good!'},
{
status: 'rejected',
reason: Error: No good, sorry!
}
]
async/await 的错误处理
JavaScript 中的 await
示意异步函数,但从维护者的角度来看,它们受害于同步函数的所有“可读性”。
为了简略起见,咱们将应用先前的同步函数 toUppercase
,并将 async
放在 function
关键字之前,将其转换为异步函数:
async function toUppercase(string) {if (typeof string !== "string") {throw TypeError("Wrong type given, expected a string");
}
return string.toUpperCase();}
只需在函数后面加上 async
,就能够使函数 返回一个 Promise。这意味着咱们能够在函数调用之后链接 then
,catch
和 finally
:
async function toUppercase(string) {if (typeof string !== "string") {throw TypeError("Wrong type given, expected a string");
}
return string.toUpperCase();}
toUppercase("abc")
.then(result => console.log(result))
.catch(error => console.error(error.message))
.finally(() => console.log("Always runs!"));
当咱们从异步函数中抛出异样时,异样会成为导致底层 Promise 被回绝的起因。
任何谬误都能够通过内部的 catch
来拦挡。
最重要的是,除了这种款式外,还能够应用 try/catch/finally
,就像应用同步函数一样。
在上面的例子中,咱们从另一个函数 consumer
调用 toUppercase
,该函数用 try/catch/finally
不便地包装函数调用:
async function toUppercase(string) {if (typeof string !== "string") {throw TypeError("Wrong type given, expected a string");
}
return string.toUpperCase();}
async function consumer() {
try {await toUppercase(98);
} catch (error) {console.error(error.message);
} finally {console.log("Always runs!");
}
}
consumer(); // Returning Promise ignored
输入为:
Wrong type given, expected a string
Always runs!
异步生成器的错误处理
JavaScript 中的 异步生成器(Async generators) 不是生产简略值,而是可能生成 Promise 的生成器函数。
它们将生成器函数与 async
联合在一起。其后果是生成器函数将 Promise 裸露给使用者的迭代器对象。
咱们用前缀为 async
和星号 *
申明一个异步生成器函数。
async function* asyncGenerator() {
yield 33;
yield 99;
throw Error("Something went wrong!"); // Promise.reject
}
基于 Promise 用于错误处理的雷同规定,异步生成器中的 throw
导致 Promise 回绝,用 catch
进行拦挡。
有两种办法能够 把 Promise 拉出异步生成器:
then
。- 异步迭代。
从下面的例子中,在前两个 yield
之后会有一个例外。这意味着咱们能够做到:
const go = asyncGenerator();
go.next().then(value => console.log(value));
go.next().then(value => console.log(value));
go.next().catch(reason => console.error(reason.message));
这段代码的输入是:
{value: 33, done: false}
{value: 99, done: false}
Something went wrong!
另一种办法是应用 异步迭代 和 for await...of
。要应用异步迭代,须要用 async
函数包装使用者。
这是残缺的例子:
async function* asyncGenerator() {
yield 33;
yield 99;
throw Error("Something went wrong!"); // Promise.reject
}
async function consumer() {for await (const value of asyncGenerator()) {console.log(value);
}
}
consumer();
和 async/await
一样,能够用 try/catch
解决任何潜在的异样:
async function* asyncGenerator() {
yield 33;
yield 99;
throw Error("Something went wrong!"); // Promise.reject
}
async function consumer() {
try {for await (const value of asyncGenerator()) {console.log(value);
}
} catch (error) {console.error(error.message);
}
}
consumer();
这段代码的输入是:
33
99
Something went wrong!
从异步生成器函数返回的迭代器对象也有一个 throw()
办法,十分相似于它的同步对象。
在这里的迭代器对象上调用 throw()
不会引发异样,然而会被 Promise 回绝:
async function* asyncGenerator() {
yield 33;
yield 99;
yield 11;
}
const go = asyncGenerator();
go.next().then(value => console.log(value));
go.next().then(value => console.log(value));
go.throw(Error("Let's reject!"));
go.next().then(value => console.log(value)); // value is undefined
能够通过执行以下操作从内部解决这种状况:
go.throw(Error("Let's reject!")).catch(reason => console.error(reason.message));
然而,别忘了迭代器对象 throw()
在生成器外部发送异样。这意味着咱们还能够用以下模式:
async function* asyncGenerator() {
try {
yield 33;
yield 99;
yield 11;
} catch (error) {console.error(error.message);
}
}
const go = asyncGenerator();
go.next().then(value => console.log(value));
go.next().then(value => console.log(value));
go.throw(Error("Let's reject!"));
go.next().then(value => console.log(value)); // value is undefined
Node.js 中的错误处理
Node.js 中的同步错误处理
Node.js 中的同步错误处理与到目前为止所看到的并没有太大差别。
对于 同步代码,try/catch/finally
能够失常工作。
然而如果进入异步世界,事件就会变得乏味。
Node.js 中的异步错误处理:回调模式
对于异步代码,Node.js 强烈依赖于两个习惯用法:
- 回调模式。
- 事件发射器(event emitter)。
在回调模式中,异步 Node.js API 承受通过事件循环解决的函数,并在调用栈为空时立刻执行。
看上面的代码:
const {readFile} = require("fs");
function readDataset(path) {readFile(path, { encoding: "utf8"}, function(error, data) {if (error) console.error(error);
// 解决数据
});
}
如果从这个清单中提取回调,则能够看到应该如何处理错误:
//
function(error, data) {if (error) console.error(error);
// 解决数据
}
//
如果通过应用 fs.readFile
读取给定门路而引起任何谬误,将失去一个谬误对象。
在这一点上,咱们能够:
- 简略地把谬误对象输入到日志。
- 引发异样。
- 将谬误传递给另一个回调。
要抛出异样,能够执行以下操作:
const {readFile} = require("fs");
function readDataset(path) {readFile(path, { encoding: "utf8"}, function(error, data) {if (error) throw Error(error.message);
// do stuff with the data
});
}
然而,与 DOM 中的事件和计时器一样,这个异样将会 使程序解体。上面的代码尝试通过 try/catch
的解决将不起作用:
const {readFile} = require("fs");
function readDataset(path) {readFile(path, { encoding: "utf8"}, function(error, data) {if (error) throw Error(error.message);
// do stuff with the data
});
}
try {readDataset("not-here.txt");
} catch (error) {console.error(error.message);
}
如果不想使程序解体,则 首选项是将谬误传递给另一个回调:
const {readFile} = require("fs");
function readDataset(path) {readFile(path, { encoding: "utf8"}, function(error, data) {if (error) return errorHandler(error);
// do stuff with the data
});
}
顾名思义,errorHandler
是一个简略的谬误处理函数:
function errorHandler(error) {console.error(error.message);
// do something with the error:
// - write to a log.
// - send to an external logger.
}
Node.js 中的异步错误处理:事件发射器
咱们在 Node.js 中所做的大部分工作都是基于 事件 的。在大多数状况下,须要与 发射器对象 和一些观察者侦听音讯进行交互。
Node.js 中的任何事件驱动模块(例如 net)都会扩大名为 EventEmitter 的根类。
Node.js 中的 EventEmitter
有两种根本办法:on
和 emit
。
看上面这个简略的 HTTP 服务器:
const net = require("net");
const server = net.createServer().listen(8081, "127.0.0.1");
server.on("listening", function () {console.log("Server listening!");
});
server.on("connection", function (socket) {console.log("Client connected!");
socket.end("Hello client!");
});
在这里,咱们监听两个事件:listening 和 connection。
除了这些事件之外,事件发射器还裸露了 error 事件,以防产生谬误。
在 80 端口上运行代码,会失去一个异样:
const net = require("net");
const server = net.createServer().listen(80, "127.0.0.1");
server.on("listening", function () {console.log("Server listening!");
});
server.on("connection", function (socket) {console.log("Client connected!");
socket.end("Hello client!");
});
输入:
events.js:291
throw er; // Unhandled 'error' event
^
Error: listen EACCES: permission denied 127.0.0.1:80
Emitted 'error' event on Server instance at: ...
要捕捉它,能够为 error 注册一个事件处理函数:
server.on("error", function(error) {console.error(error.message);
});
这将会输入:
listen EACCES: permission denied 127.0.0.1:80
并且程序不会解体。
总结
在本文中,咱们介绍了 从简略的同步代码到高级异步原语,以及整个 JavaScript 的错误处理。
在 JavaScript 程序中,能够通过多种形式来显示异样。
同步代码中的异样是最容易捕捉的。而来自 异步代码 门路的 异样 解决可能会有些辣手。
同时,浏览器中的新 JavaScript API 简直都朝着 Promise
的方向倒退。这种广泛的模式使得用 then/catch/finally
或用 try/catch
来解决 async/await
异样更加容易。
看完本文后,你应该可能辨认程序中可能会呈现的所有不同状况,并正确捕捉异样。
本文首发微信公众号:前端先锋
欢送扫描二维码关注公众号,每天都给你推送陈腐的前端技术文章
欢送持续浏览本专栏其它高赞文章:
- 深刻了解 Shadow DOM v1
- 一步步教你用 WebVR 实现虚拟现实游戏
- 13 个帮你进步开发效率的古代 CSS 框架
- 疾速上手 BootstrapVue
- JavaScript 引擎是如何工作的?从调用栈到 Promise 你须要晓得的所有
- WebSocket 实战:在 Node 和 React 之间进行实时通信
- 对于 Git 的 20 个面试题
- 深刻解析 Node.js 的 console.log
- Node.js 到底是什么?
- 30 分钟用 Node.js 构建一个 API 服务器
- Javascript 的对象拷贝
- 程序员 30 岁前月薪达不到 30K,该何去何从
- 14 个最好的 JavaScript 数据可视化库
- 8 个给前端的顶级 VS Code 扩大插件
- Node.js 多线程齐全指南
- 把 HTML 转成 PDF 的 4 个计划及实现
- 更多文章 …