乐趣区

关于前端:关于-JavaScript-错误处理的最完整指南下半部

作者:Valentino Gagliardi
译者:前端小智
起源:valentinog

点赞再看,微信搜寻 【大迁世界】 关注这个没有大厂背景,但有着一股向上踊跃心态人。本文 GitHub https://github.com/qq44924588… 上曾经收录,文章的已分类,也整顿了很多我的文档,和教程材料。

大家都说简历没我的项目写,我就帮大家找了一个我的项目,还附赠【搭建教程】。

应用 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 来捕捉呈现的谬误。

toUppercase(99)
  .then(result => result)
  .catch(error => console.error(error.message));

下面的执行后果:

Wrong type given, expected a string

除了 thencatch , Promise 中还有 finally 办法,这相似于try/catch 中的 finally

toUppercase(99)
  .then(result => result)
  .catch(error => console.error(error.message))
  .finally(() => console.log("Run baby, run"));

Promise, error, 和 throw

应用 Promise.reject 能够很不便的抛出谬误:

Promise.reject(TypeError("Wrong type given, expected a string"));

除了Promise.reject,咱们也能够通过抛出异样来退出 Promise。

思考以下示例:

Promise.resolve("A string").then(value => {if (typeof value === "string") {throw TypeError("Expected a number!");
  }
});

要进行异样流传,咱们照常应用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 拦挡异样。如果咱们失败了,或者决定不捕捉它,异样能够在堆栈中自在冒泡。

应用 Promise 来解决定时器中的异样

应用定时器或事件无奈捕捉从回调引发的异样。

function failAfterOneSecond() {setTimeout(() => {throw Error("Something went wrong!");
  }, 1000);
}

// DOES NOT WORK
try {failAfterOneSecond();
} catch (error) {console.error(error.message);
}

解决方案就是应用 Promise:

function failAfterOneSecond() {return new Promise((_, reject) => {setTimeout(() => {reject(Error("Something went wrong!"));
    }, 1000);
  });
}

应用reject,咱们启动了一个 Promise 回绝,它携带一个谬误对象。

此时,咱们能够应用 catch 解决异样:

failAfterOneSecond().catch(reason => console.error(reason.message));

应用 Promise.all 来处理错误

Promise.all(iterable) 办法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“实现(resolved)”或参数中不蕴含 promise 时回调实现(resolve);

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 有一个失败(rejected),此实例回调失败(reject),失败的起因是第一个失败 promise 的后果。

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));

// No good, sorry!

同样,无论 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 可迭代对象,只有其中的一个 promise 胜利,就返回那个曾经胜利的 promise。如果可迭代对象中没有一个 promise 胜利(即所有的 promises 都失败 / 回绝),就返回一个失败的 promiseAggregateError 类型的实例,它是 Error 的一个子类,用于把繁多的谬误汇合在一起。实质上,这个办法和 Promise.all() 是相同的。

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

此属性是由 reject 产生的每个独自谬误的数组

[Error: "No good, sorry!, Error:"Bad day ..."]

应用 Promise.race 来处理错误

Promise.race(iterable) 办法返回一个 promise,一旦迭代器中的某个 promise 解决或回绝,返回的 promise 就会解决或回绝。

const promise1 = Promise.resolve("The first!");
const promise2 = Promise.resolve("The second!");

Promise.race([promise1, promise2]).then(result => console.log(result));

// The first!

这里阐明,第一个 Promise 比第二个行执行完。那蕴含回绝的状况又是怎么样的?

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!

如果把 reject 放在第一个又会怎么样?

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()办法返回一个在所有给定的 promise 都曾经 fulfilledrejected后的 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.allSettled 一个由两个 Promise 组成的数组:一个已解决,另一个被回绝。

这种状况 catch 不会被执行,finally 永远会执行。

[{ status: 'fulfilled', value: 'Good!'},
  {
    status: 'rejected',
    reason: Error: No good, sorry!
  }
]

应用 async/await 来处理错误

为了简略起见,咱们应用后面的同步函数 toUppercase,并通过在function 关键字前搁置 async 来将其转换为异步函数

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!"));

当从 async 函数抛出异样时,咱们就能够应用 catch 来捕捉。

最重要的是,除了这种形式外,咱们能够还应用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(); 

输入:

Wrong type given, expected a string
Always runs!

应用 async generators 来处理错误

JavaScript 中的 async generators 是可能生成 Promises 而不是简略值的生成器函数。

async function* asyncGenerator() {
  yield 33;
  yield 99;
  throw Error("Something went wrong!"); // Promise.reject
}

基于 Promise,此处实用于错误处理的雷同规定。在异步生成器中 throw 将会触发 Promise 的 reject,咱们能够应用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 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));

Node 中的错误处理

Node 中的同步错误处理

Node.js 中的同步错误处理与到目前为止所看到的并没有太大差别。对于同步,应用 try/catch/finally 就能够很好的工作了。

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

对于异步代码,Node.js 次要应用这两种形式:

  • 回调模式
  • event emitters

在回调模式中,异步 Node.js API 承受一个函数,该函数通过事件循环解决,并在调用堆栈为空时立刻执行。

思考以下代码:

const {readFile} = require("fs");

function readDataset(path) {readFile(path, { encoding: "utf8"}, function(error, data) {if (error) console.error(error);
    // do stuff with the data
  });
}

咱们能够看到,这里处理错误的形式是应用了回调:

//
function(error, data) {if (error) console.error(error);
    // do stuff with the data
  }
//

如果应用 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 中的异步错误处理:event emitters

在 Node.js 中所做的大部分工作都是基于 事件 的。大多数状况下,emitter object 和一些观察者进行交互以侦听音讯。

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。除了这些事件之外,event emitters 还公开一个 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 简直都偏差 Promisethen/catch/finallytry/catch 的模式对于 async/await 的异样解决变得更加容易。


代码部署后可能存在的 BUG 没法实时晓得,预先为了解决这些 BUG,花了大量的工夫进行 log 调试,这边顺便给大家举荐一个好用的 BUG 监控工具 Fundebug。

原文:https://www.valentinog.com/bl…

交换

文章每周继续更新,能够微信搜寻 【大迁世界】 第一工夫浏览,回复 【福利】 有多份前端视频等着你,本文 GitHub https://github.com/qq449245884/xiaozhi 曾经收录,欢送 Star。

退出移动版