Skip to main content

Others

JavaScript Error Handling

Basic Error Handling

Use try…catch to handle errors that occur while executing code.

try {
const response = await fetch(`/api/users/${userId}`)
return await response.json()
} catch (error) {
console.log(error.message)
}

The catch block receives the thrown error object, allowing you to inspect properties such as message or name.

Creating Custom Errors

Custom error classes make it easier to identify and handle specific types of errors.

class ValidationError extends Error {
constructor(message) {
super(message)
this.name = 'ValidationError'
}
}

class NetworkError extends Error {
constructor(message, statusCode) {
super(message)
this.name = 'NetworkError'
this.statusCode = statusCode
}
}

Usage:

throw new ValidationError('Invalid email')

A Better Custom Error Pattern

Instead of manually setting the name property in every error class, create a common base class.

class AppError extends Error {
constructor(message, options) {
super(message, options)
this.name = this.constructor.name
}
}

class ValidationError extends AppError {}
class DatabaseError extends AppError {}
class NetworkError extends AppError {}

Now every subclass automatically gets the correct error name.

throw new ValidationError('Invalid email')

// error.name === "ValidationError"

Handling Different Error Types

Use instanceof to handle different errors appropriately.

try {
await saveUser(userData)
} catch (error) {
if (error instanceof ValidationError) {
// Show validation message to the user
showFieldErrors(error.fields)

} else if (error instanceof NetworkError) {
// Network issue - maybe retry
showRetryButton()

} else {
// Unknown error
console.error('Unexpected error:', error)
showGenericError()
}
}

Always Check response.ok with fetch

  • A common mistake is assuming that fetch() throws an error for HTTP status codes like 404 or 500.
  • It doesn't.
    • fetch() only throws for network-related failures. For HTTP errors, you must check response.ok yourself.
  • Incorrect
try {
const response = await fetch('/api/users/999')
const user = await response.json()
} catch (error) {
// Only catches network errors
}
  • Correct
try {
const response = await fetch('/api/users/999')

if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`)
}

const user = await response.json()
} catch (error) {
// Catches both network and HTTP errors
console.error('Request failed:', error.message)
}

JavaScript Regular Expressions (RegExp)

  • Regular expressions (RegExp) are patterns used to search, validate, extract, and replace text.

test() Vs match()

These are two of the most commonly used regular expression methods.

  • test()
    • Returns a boolean indicating whether the pattern matches.
const emailPattern = /\S+@\S+\.\S+/

console.log(emailPattern.test('user@example.com')) // true
console.log(emailPattern.test('userexample.com')) // false
  • match()
    • Returns an array containing the matched text (or null if no match is found).
    • Using the g (global) flag returns all matches instead of only the first.
const text = 'Order IDs: 123, 456, 789'

console.log(text.match(/\d+/g))
// ["123", "456", "789"]

search()

  • Returns the index where the first match starts.
  • If no match is found, it returns -1.
const text = 'Hello World'

console.log(text.search(/World/)) // 6
console.log(text.search(/xyz/)) // -1

replace()

  • Replace matching text with another value.
  • Using the g flag replaces all occurrences.
console.log('hello world'.replace(/o/g, '0'))

// "hell0 w0rld"

split()

  • Regular expressions can also be used with split().
// The pattern removes optional whitespace around commas.
const items = 'a, b,c , d'.split(/\s*,\s*/)

console.log(items)

// ["a", "b", "c", "d"]

Named Capture Groups

  • Named capture groups make matched values easier to access.
  • Use the following syntax:
(?<name>pattern)
  • Example:
const datePattern = /(?<month>\d{2})-(?<day>\d{2})-(?<year>\d{4})/

const match = '12-25-2024'.match(datePattern)

console.log(match.groups.month) // "12"
console.log(match.groups.day) // "25"
console.log(match.groups.year) // "2024"

Greedy Vs Lazy Matching

  • By default, quantifiers are greedy. They match as much text as possible.
  • Adding ? makes a quantifier lazy, meaning it matches as little text as possible.
  • Example:
const html = '<div>Hello</div><div>World</div>'

Greedy

Matches everything between the first <div> and the last </div>.

console.log(html.match(/<div>.*<\/div>/)[0])

// "<div>Hello</div><div>World</div>"

Lazy

Stops at the first closing </div>.

console.log(html.match(/<div>.*?<\/div>/)[0])

// "<div>Hello</div>"