关于javascript:JavaScript-错误处理大全

4次阅读

共计 18562 个字符,预计需要花费 47 分钟才能阅读完成。

编程中有什么谬误?

在咱们的程序中,事件并非一帆风顺

特地是在某些状况下,咱们可能心愿在 进行程序或在产生不良情况时告诉用户
例如:

  • 程序试图关上一个不存在的文件。
  • 网络连接断开。
  • 用户进行了有效的输出。

在所有的这些状况下,咱们作为程序员都会产生 谬误,或者让编程引擎为咱们创立一些谬误。

在创立谬误之后,咱们能够向用户告诉音讯,或者能够齐全进行执行。

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 还实现了一堆非标准属性,例如 columnNumberfilenamelineNumber

JavaScript 中的谬误类型

JavaScript 中有很多类型的谬误,即:

  • Error
  • EvalError
  • InternalError
  • RangeError
  • ReferenceError
  • SyntaxError
  • TypeError
  • URIError

请记住,所有这些谬误类型都是 理论构造函数,旨在返回一个新的谬误对象。

在代码中次要用 ErrorTypeError 这两种最常见的类型来创立本人的谬误对象。

然而在大多数状况下,很多谬误间接来自 JavaScript 引擎,例如 InternalErrorSyntaxError

上面的例子是当你尝试从新为 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.messageerror.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 节点连贯到 EventTargetEventTarget 是浏览器中所有 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 元素具备许多事件处理函数,例如 onclickonmouseenteronchange 等。

还有 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);
});

此模式可用于 加载代替资源来替换失落的图像或脚本

然而要记住:onerrorthrowtry/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.rejectPromise.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 是用于处理错误的构造。

除了 catchthen 之外,还有 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。这意味着咱们能够在函数调用之后链接 thencatchfinally

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 有两种根本办法:onemit

看上面这个简略的 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!");
});

在这里,咱们监听两个事件:listeningconnection

除了这些事件之外,事件发射器还裸露了 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 个计划及实现

  • 更多文章 …
正文完
 0