阿里云主机折上折
  • 微信号
Current Site:Index > Error handling pattern

Error handling pattern

Author:Chuan Chen 阅读数:45704人阅读 分类: JavaScript

ECMAScript 6 Error Handling Patterns

ECMAScript 6 introduced more robust error handling mechanisms, including Promise exception catching, optimized this binding in arrow functions, and new Error subclasses. These features make error handling in asynchronous and synchronous code more unified and flexible.

Promise Error Handling

Promises handle errors in asynchronous operations through the .catch() method. Compared to traditional callback functions, Promise error handling is more intuitive:

fetch('https://api.example.com/data')
  .then(response => {
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    return response.json();
  })
  .catch(error => {
    console.error('Fetch error:', error);
    // Error recovery or reporting can be done here
  });

Errors in a Promise chain propagate downward until caught by .catch(). ES6 also introduced Promise.reject() to quickly create a rejected Promise:

function getUser(id) {
  if (!id) {
    return Promise.reject(new Error('Invalid user ID'));
  }
  return fetch(`/users/${id}`);
}

try-catch with async/await

The async/await syntax allows synchronous-style try-catch blocks for handling errors in asynchronous code:

async function loadData() {
  try {
    const response = await fetch('/api/data');
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error('Failed to load data:', error);
    // Error recovery can be performed here
  }
}

This pattern is particularly suitable for scenarios requiring sequential processing of multiple asynchronous operations, with more centralized error handling logic.

Error Class Extensions

ES6 allows creating custom error types through class inheritance, which is highly useful in large applications:

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

function validateUser(user) {
  if (!user.name) {
    throw new ValidationError('Name is required', 'name');
  }
}

try {
  validateUser({});
} catch (error) {
  if (error instanceof ValidationError) {
    console.error(`Validation failed for ${error.field}:`, error.message);
  } else {
    console.error('Unexpected error:', error);
  }
}

finally Clause

ES6 supports the finally clause in both Promises and try-catch blocks for executing cleanup code that must run regardless of success or failure:

let isLoading = true;

fetch('/data')
  .then(handleData)
  .catch(handleError)
  .finally(() => {
    isLoading = false;
    console.log('Request completed');
  });

In synchronous code:

let resource;
try {
  resource = acquireResource();
  // Use the resource
} catch (error) {
  console.error(error);
} finally {
  if (resource) {
    resource.release();
  }
}

Global Error Handling

For unhandled Promise rejections, global handlers can be added:

window.addEventListener('unhandledrejection', event => {
  console.error('Unhandled rejection:', event.reason);
  // Errors can be reported here
  event.preventDefault(); // Prevent default browser console error
});

For conventional errors:

window.onerror = function(message, source, lineno, colno, error) {
  console.error('Global error:', { message, source, lineno, error });
  // Return true to prevent default error prompts
  return true;
};

Error Boundary Pattern

In complex applications, the error boundary pattern can isolate component-level errors:

class ErrorBoundary {
  constructor(app) {
    this.app = app;
  }

  async run() {
    try {
      await this.app.start();
    } catch (error) {
      this.handleError(error);
    }
  }

  handleError(error) {
    // Log detailed error information
    console.error('Application error:', error);
    // Display a user-friendly error interface
    showErrorUI();
    // Report the error to the server
    reportError(error);
  }
}

Error Translation Pattern

When dealing with multiple layers of abstraction, the error translation pattern can preserve original error information:

class DataService {
  async getData() {
    try {
      return await fetchData();
    } catch (rawError) {
      throw new DataServiceError('Failed to fetch data', {
        cause: rawError
      });
    }
  }
}

class DataServiceError extends Error {
  constructor(message, { cause }) {
    super(message);
    this.cause = cause;
  }
}

Defensive Programming Practices

Combining ES6 features enables more robust defensive programming:

function parseJSONSafely(jsonString) {
  try {
    return JSON.parse(jsonString);
  } catch {
    // Use null instead of throwing an error
    return null;
  }
}

// Use default parameters and nullish coalescing
function createUser({ name = 'Anonymous', age } = {}) {
  return {
    name,
    age: age ?? 18
  };
}

Performance Considerations

Error handling significantly impacts performance, especially in hot-path code:

// Avoid frequent try-catch in performance-critical code
function processItems(items) {
  // Checking first and then processing is more efficient than direct try-catch
  if (!Array.isArray(items)) {
    return [];
  }

  return items.map(item => transformItem(item));
}

Enhanced Debugging

Leverage the stack property of Error objects to enhance debugging:

function assert(condition, message) {
  if (!condition) {
    const error = new Error(message || 'Assertion failed');
    console.error(error.stack);
    throw error;
  }
}

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

上一篇:await表达式

下一篇:串行与并行执行

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.