call stack是什么错误_JavaScript 错误处理大全「值得收藏」

0d13200f7a49adddd708e39c3c20ccf3.png

作者:疯狂的技术宅

转发链接:https://mp.weixin.qq.com/s/-cpUkuzpsPR6uUgWPkbH-A

目录

编程中有什么错误?

JavaScript 中有什么错误?

JavaScript 中的错误类型

什么是异常?

当抛出异常时会发生什么?

同步错误处理

常规函数的错误处理

生成器函数的错误处理

异步错误处理

计时器错误处理

事件的错误处理

How about onerror?

怎么处理 onerror?

用 Promise 处理错误

Promise, error 和 throw

错误处理 “promisified” 计时器

Promise.all 中的错误处理

Promise.any 中的错误处理

Promise.race 中的错误处理

Promise.allSettled 中的错误处理

async/await 的错误处理

异步生成器的错误处理

Node.js中的错误处理

Node.js 中的同步错误处理

Node.js 中的异步错误处理:回调模式

Node.js 中的异步错误处理:事件发射器

总结

编程中有什么错误?

在我们的程序中,事情并非一帆风顺

特别是在某些情况下,我们可能希望在停止程序或在发生不良状况时通知用户。例如:

  • 程序试图打开一个不存在的文件。
  • 网络连接断开。
  • 用户进行了无效的输入。

在所有的这些情况下,我们作为程序员都会产生错误,或者让编程引擎为我们创建一些错误。

在创建错误之后,我们可以向用户通知消息,或者可以完全停止执行。

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     http://localhost:5000/index.js:9

另外还可以看到发生错误的代码行数。

这段报告是 栈跟踪(stack trace),对于跟踪代码中的问题很有帮助。

栈跟踪从底部到顶部。所以是这样的:

    toUppercase http://localhost:5000/index.js:3     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; // 33const secondStep = go.next().value; // 99

生成器也可以通过其他方式工作:它们可以接受调用者返回的值和异常

除了 next() 外,从生成器返回的迭代器对象还有 throw() 方法。用这个方法,可以通过把异常注入到生成器来暂停程序:

function* generate() {  yield 33;  yield 99;}const go = generate();const firstStep = go.next().value; // 33go.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:3399Tired 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/catchTrack 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/catchTrack B: --> addEventListener --> callback --> throw

如果不想使程序崩溃,为了正确处理错误,必须把 try/catch 放在 addEventListener 的回调内。但这样做没有任何价值。与 setTimeout 一样,异步代码路径引发的异常从外部是无法捕获的,这将会使程序崩溃。

How about onerror?

怎么处理 onerror?

HTML 元素具有许多事件处理函数,例如 onclick、onmouseenter 和 onchange 等。

还有 onerror,但是它与 throw 没有什么关系。

每当像 标签或

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值