javascript错误
It is important to keep in consideration the different scenarios in which errors could occur while writing your code. They play an integral part in your web application because you can avoid deadly scenarios (like your application crashing completely) that would otherwise not have been avoided if left unhandled.
重要的是要考虑编写代码时可能发生错误的不同情况。 它们在您的Web应用程序中起着不可或缺的作用,因为您可以避免致命的情况(例如您的应用程序完全崩溃),否则将无法避免。
This article will go over some best practices for controlling errors and handling them in JavaScript.
本文将介绍一些用于控制错误并在JavaScript中进行处理的最佳实践。
扩展错误 (Extending the Error)
It’s often useful to provide a more descriptive error inside your error handlers. And by this, I don’t just mean writing your error message clearer. What I’m referring to is to extend the Error
class.
在错误处理程序中提供更具描述性的错误通常很有用。 而且,这不仅意味着编写更清晰的错误消息。 我指的是扩展Error
类。
By doing this, you can customize the name
and message
property that will be useful for debugging as well as attach custom getters, setters, and methods that need it:
通过执行此操作,您可以定制将对调试有用的name
和message
属性,并附加需要它的定制getter,setter和方法:
class BadParametersError extends Error {
name = 'BadParametersError'
constructor(message) {
super(message)
}
get recommendation() {
return this._recommendation
}
set recommendation(recommendation) {
this._recommendation = recommendation
}
}
This can provide a smoother debugging experience — especially if you have multiple blocks of code that throw similarly. When multiple scenarios in your code can throw for the same reasons and need some additional context, it can become a little difficult to go back and find out exactly why it was thrown without a utility (depending on the kind of error it is, of course).
这样可以提供更流畅的调试体验-尤其是当您有多个类似抛出的代码块时。 当代码中的多个场景可能出于相同的原因而抛出并且需要一些其他上下文时,返回并确切查明为什么没有实用程序就抛出它的原因可能会有些困难(当然,这取决于错误的类型) )。
Let’s go over a scenario where this becomes useful.
让我们来看一个有用的场景。
Let’s say you have a function that takes a list of resolver functions. The function takes an argument, and when it runs, it will loop through the resolvers and pass the argument to each function. If a function returns a result, then it stops the loop and returns the result:
假设您有一个需要解析程序功能列表的函数。 该函数接受一个参数,当它运行时,它将遍历解析器,并将该参数传递给每个函数。 如果一个函数返回结果,则它停止循环并返回结果:
// Takes a list of resolvers, composes them and returns a func that calls
// each resolvers on the provided args.
function composeResolvers(...resolvers) {
return (args) => {
let result
for (let index = 0; index < resolvers.length; index++) {
const resolve = resolvers[index]
result = resolve(args)
if (result) {
break // Abort the loop since we now found a value
}
}
return result
}
}
Now let’s pretend we are building a page that prompts a user to enter the year they were born before assigning them to some group based on their age:
现在,让我们假设我们正在构建一个页面,该页面会提示用户输入他们的出生年份,然后根据他们的年龄将他们分配给某个组:
import composeResolvers from '../composeResolvers'
const resolvers = []
const someResolverFn = (userInput) => {
if (userInput > 2002) {
return 'NewKidsOnTheBlock'
}
return 'OldEnoughToVote'
}
// Pretending our code is only stable/supported by certain browsers
if (/chrome/i.test(navigator.userAgent)) {
resolvers.push(someResolverFn)
}
const resolve = composeResolvers(...resolvers)
window.addEventListener('load', () => {
const userInput = window.prompt('What year was your computer created?')
const result = resolve(userInput)
window.alert(`We recommend that you register for the group: ${result}`)
})
This code prompts the user, and when they click OK, their age gets assigned to userInput
and gets sent as an argument to the function produced by composeResolvers
:
这段代码提示用户,当他们单击OK时,他们的年龄分配给userInput
并作为参数发送给userInput
产生的composeResolvers
:

And when it finishes, it runs window.alert
to show the user the computed group:
完成后,它将运行window.alert
向用户显示计算出的组:

This is working fine, but what if the user wasn’t using the Chrome browser? That means this line wouldn’t run:
一切正常,但是如果用户未使用Chrome浏览器怎么办? 这意味着该行将不会运行:
resolvers.push(someResolverFn)
This generates an embarrassing unexpected result:
这会产生令人尴尬的意外结果:

We can prevent unhandled errors like these by throwing a normal Error
or we can use the more specific BadParametersError
:
我们可以通过抛出普通的Error
来防止此类未处理的Error
,也可以使用更具体的BadParametersError
:
// Takes a list of resolvers, composes them and returns a func that calls
// each resolvers on the provided args.
function composeResolvers(...resolvers) {
if (!resolvers.length) {
const err = new BadParametersError(
'Need at least one function to compose resolvers',
)
err.recommendation =
'Provide a function that takes one argument and returns a value'
throw err
}
return (args) => {
let result
for (let index = 0; index < resolvers.length; index++) {
const resolve = resolvers[index]
result = resolve(args)
if (result) {
break // Abort the loop since we now found a value
}
}
return result
}
}
This way, the error has a much lower chance of getting to the user and it makes the developer fix the mistake:
这样,错误获得用户的机会要低得多,这会使开发人员修复错误:

If multiple functions use this strategy and are attached a property like recommended
for example, the debugging experience becomes much easier.
如果多个函数使用此策略并附加了例如recommended
的属性,则调试体验将变得更加容易。
Now it can be written to something like this, which is a better defense mechanism:
现在可以将其写成这样,这是一种更好的防御机制:
const resolve = composeResolvers(...resolvers)
window.addEventListener('load', () => {
const userInput = window.prompt('What year was your computer brought to you?')
let result
try {
result = resolve(userInput)
} catch (error) {
if (error instanceof BadParametersError) {
console.error(
`[Error] ${error.message}. Here's a recommendation: ${error.recommendation}`,
)
console.log(error.recommendation)
} else {
// Do some fallback logic
return window.alert(
'We are sorry, there was a technical problem. Please come back later',
)
}
}
window.alert(`We recommend that you register for the group: ${result}`)
})
使用TypeError
(Use a TypeError
)
We often handle errors by throwing Error
. But when built-in JavaScript errors are available to narrow the scenario when the opportunity arrives, it’s useful to just leverage them:
我们通常通过抛出Error
处理Error
。 但是,当机会到来时,当内置JavaScript错误可用来缩小场景时,仅利用它们会很有用:
async function fetchDogs(id) {
let result
if (typeof id === 'string') {
result = await api.fetchDogs(id)
} else if (typeof id === 'array') {
result = await Promise.all(id.map((str) => api.fetchDogs(id)))
} else {
throw new TypeError(
'callSomeApi only accepts a string or an array of strings',
)
}
return result
}
const params = { id: 'doggie123' }
let dogs
fetchDogs(params)
.then((dogs) => {
dogs = dogs
})
.catch((err) => {
if (err instanceof TypeError) {
dogs = Promise.resolve(fetchDogs(params.id))
} else {
throw err
}
})
测试中(Testing)
By leveraging derived Errors
, testing becomes more robust, as you're able to directly use them to make your assertions:
通过利用派生的Errors
,测试将变得更加健壮,因为您可以直接使用它们进行断言:
import { expect } from 'chai'
import chaiAsPromised from 'chai-as-promised'
import fetchCats from '../fetchCats'
chai.use(chaiAsPromised)
it('should only take in arrays', () => {
expect(fetchCats('abc123')).to.eventually.rejectWith(TypeError)
})
过度(Overdoing It)
Knowing how useful it is, it also becomes tempting to just create a bunch of custom errors for every possible scenario you can think of — especially when you’re just beginning to learn JavaScript. So if you have a small to medium-size app and end up in a situation like this:
知道它的用处之后,也很容易为您可能想到的每种情况创建一堆自定义错误,尤其是在您刚开始学习JavaScript时。 因此,如果您有一个中小型应用程序,并最终遇到以下情况:
class AbortExecuteError extends Error {
name = 'AbortExecuteError'
constructor(message) {
super(message)
}
}
class BadParameters extends Error {
name = 'BadParameters'
constructor(message) {
super(message)
}
}
class TimedOutError extends Error {
name = 'TimedOutError'
constructor(message) {
super(message)
}
}
class ArrayTooLongError extends Error {
name = 'ArrayTooLongError'
constructor(message) {
super(message)
}
}
class UsernameError extends Error {
name = 'UsernameError'
constructor(message) {
super(message)
}
}
Then you might rethink this approach and determine if you really need to do this or not. Most of the time, it’s enough to provide a clearer error message
. Making assertions about custom errors only provides maximum benefit when you need to add some extra context to it (e.g. a token to retry on a timed-out request):
然后,您可能会重新考虑这种方法,并确定您是否真的需要这样做。 在大多数情况下,提供一个更清晰的错误message
就足够了。 关于自定义错误的断言仅在您需要向其添加一些额外的上下文(例如,对超时请求重试的令牌)时才提供最大的好处:
class TimedOutError extends Error {
name = 'TimedOutError'
retried = 0
constructor(message) {
super(message)
}
set retry(callback) {
this._retry = callback
}
retry(...args) {
this.retried++
return this._retry(...args)
}
}
class ConnectToRoomTimedOutError extends TimedOutError {
name = 'ConnectToRoomTimedOutError'
constructor(message) {
super(message)
}
get token() {
return this._token
}
set token(token) {
this._token = token
}
}
let timeoutRef
async function connect(token) {
if (timeoutRef) clearTimeout(timeoutRef)
timeoutRef = setTimeout(() => {
const err = new ConnectToRoomTimedOutError(
'Did not receive a response from the server',
)
err.retry = connect
err.token = token
throw err
}, 10000)
const room = await api.join(token)
clearTimeout(timeoutRef)
return room
}
}
const joinRoom = () => getToken().then((token) => connect(token))
async function start() {
try {
let room = await joinRoom()
return room
} catch (err) {
if (err instanceof ConnectToRoomTimedOutError) {
try {
// Lets retry one more time
room = await err.retry(err.token)
return room
} catch (innerErr) {
throw innerError
}
}
throw err
}
}
start()
.then((room) => {
console.log(`Received room, oh yea!`, room)
})
.catch(console.error)
结论(Conclusion)
Controlling your errors provides control over your app. It also saves you time and money.
控制错误可以控制您的应用程序。 它还可以节省您的时间和金钱。
This marks the end of the article. I hope you found it useful. Look out for more in the future!
这标志着文章的结尾。 希望你觉得它有用。 希望将来有更多机会!
javascript错误