try…catch 结构:处理同步与异步代码中的错误

试错的艺术:同步与异步代码中的错误处理

引言

大家好,欢迎来到今天的讲座!今天我们要聊一聊 JavaScript 中的 try...catch 结构,以及它如何帮助我们在同步和异步代码中优雅地处理错误。如果你曾经在写代码时遇到过“神秘”的错误,或者对 try...catch 的工作原理感到困惑,那么你来对地方了!

我们将会用轻松诙谐的方式,结合一些实际的代码示例,带你深入了解 try...catch 的使用技巧。准备好了吗?让我们开始吧!

什么是 try...catch

首先,让我们回顾一下 try...catch 的基本概念。try...catch 是一种用于捕获和处理错误的结构,它的作用是让你可以在代码中“尝试”执行某些可能会出错的操作,并在出错时“捕获”这些错误,而不是让程序崩溃。

基本语法

try {
  // 可能会抛出错误的代码
} catch (error) {
  // 处理错误的代码
}
  • try 块中的代码是你认为可能会出错的部分。
  • catch 块中的代码会在 try 块中发生错误时执行,error 是一个包含错误信息的对象。

举个栗子

假设我们有一个函数,它会尝试将字符串转换为数字。如果传入的不是有效的数字字符串,就会抛出一个错误:

function parseNumber(str) {
  try {
    const num = Number(str);
    if (isNaN(num)) {
      throw new Error('Invalid number format');
    }
    return num;
  } catch (error) {
    console.error('Error:', error.message);
    return null;
  }
}

console.log(parseNumber("123")); // 输出: 123
console.log(parseNumber("abc")); // 输出: Error: Invalid number format, null

在这个例子中,try 块尝试将字符串转换为数字,如果失败了,catch 块会捕获错误并返回 null,同时打印出错误信息。

同步代码中的 try...catch

在同步代码中,try...catch 的使用非常直观。只要代码块中抛出了错误,catch 块就会立即捕获并处理它。我们刚才的例子就是一个典型的同步错误处理场景。

更多栗子:文件读取

假设我们要读取一个文件的内容,但文件可能不存在。我们可以使用 Node.js 的 fs.readFileSync 来实现这个功能:

const fs = require('fs');

try {
  const data = fs.readFileSync('nonexistent-file.txt', 'utf8');
  console.log(data);
} catch (error) {
  console.error('File not found:', error.message);
}

如果文件不存在,readFileSync 会抛出一个错误,catch 块会捕获这个错误并输出一条友好的提示信息。

异步代码中的 try...catch

事情在异步代码中变得稍微复杂一些。由于异步操作通常不会立即完成,传统的 try...catch 无法直接捕获异步代码中的错误。幸运的是,JavaScript 提供了 async/await 语法,让我们可以像处理同步代码一样处理异步代码中的错误。

async/await + try...catch

async/await 是 ES2017 引入的语法糖,它让异步代码看起来更像同步代码。结合 try...catch,我们可以非常方便地处理异步操作中的错误。

举个栗子:HTTP 请求

假设我们要从一个 API 获取数据,但 API 可能会返回错误。我们可以使用 fetch 函数来发起 HTTP 请求,并使用 try...catch 来处理可能出现的错误:

async function fetchData(url) {
  try {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    const data = await response.json();
    console.log('Data:', data);
  } catch (error) {
    console.error('Error fetching data:', error.message);
  }
}

fetchData('https://api.example.com/data');

在这个例子中,fetch 是一个异步操作,await 让我们等待它完成。如果请求失败(例如网络问题或服务器返回 404),catch 块会捕获错误并输出一条友好的提示信息。

Promise 中的 try...catch

除了 async/await,我们还可以在 Promise 链中使用 try...catch。虽然 Promise 本身有 .then().catch() 方法来处理成功和失败的情况,但在某些情况下,try...catch 仍然非常有用。

举个栗子:多个异步操作

假设我们有两个异步操作,一个是获取用户数据,另一个是获取用户的订单数据。我们可以使用 Promise.all 来并行执行这两个操作,并使用 try...catch 来处理任何可能的错误:

async function getUserAndOrders(userId) {
  try {
    const [user, orders] = await Promise.all([
      fetchUser(userId),
      fetchOrders(userId)
    ]);
    console.log('User:', user);
    console.log('Orders:', orders);
  } catch (error) {
    console.error('Error fetching user or orders:', error.message);
  }
}

function fetchUser(userId) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (userId > 0) {
        resolve({ id: userId, name: 'Alice' });
      } else {
        reject(new Error('User not found'));
      }
    }, 1000);
  });
}

function fetchOrders(userId) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (userId > 0) {
        resolve([{ id: 1, product: 'Laptop' }]);
      } else {
        reject(new Error('Orders not found'));
      }
    }, 1500);
  });
}

getUserAndOrders(1); // 成功
getUserAndOrders(-1); // 失败

在这个例子中,Promise.all 并行执行两个异步操作,try...catch 捕获任何一个操作中发生的错误。

错误优先的回调 vs try...catch

在 Node.js 中,很多旧版本的 API 使用的是“错误优先的回调”模式。这种模式要求你在回调函数中手动检查是否有错误,并进行处理。相比 try...catch,这种方式显得更加繁琐。

举个栗子:错误优先的回调

fs.readFile('file.txt', 'utf8', (error, data) => {
  if (error) {
    console.error('Error reading file:', error.message);
    return;
  }
  console.log('File content:', data);
});

相比之下,使用 async/awaittry...catch 的代码更加简洁和易读:

async function readFile(filename) {
  try {
    const data = await fs.promises.readFile(filename, 'utf8');
    console.log('File content:', data);
  } catch (error) {
    console.error('Error reading file:', error.message);
  }
}

readFile('file.txt');

总结

通过今天的讲座,我们了解了 try...catch 在同步和异步代码中的不同用法。无论是处理简单的同步错误,还是复杂的异步操作,try...catch 都是一个非常强大的工具。结合 async/await,我们可以写出更加简洁、易读且健壮的代码。

当然,try...catch 并不是万能的。在某些情况下,使用 Promise.catch() 或者错误优先的回调可能是更好的选择。但无论如何,掌握 try...catch 的使用技巧,将帮助你在编写 JavaScript 代码时更加自信地应对各种错误情况。

希望今天的讲座对你有所帮助!如果你有任何问题,欢迎在评论区留言。我们下次再见! 😊


参考资料:

  • MDN Web Docs: Try…catch
  • Node.js Documentation: Error Handling
  • You Don’t Know JS (book series) by Kyle Simpson

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注