关于js的错误处理,原来还有这么多母鸡的(上)

537 阅读4分钟

「这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战

作者: Ashish Lahoti 译者:前端很美 来源:codingnconcepts

image.png

前言

本文我们将会回顾通过使用trycatchfinally and throw来处理错误, 也会回顾内置的js错误对象(如Error、SyntaxError、ReferenceError 等),以及如何自定义错误。

1.使用 try..catch..finally..throw

在js中我们使用trycatchfinally and throw来处理错误

  1. 使用try关键字包裹代码以检查错误。
  2. 使用throw关键字用于抛出自定义错误。
  3. 使用catch关键字处理捕获的错误。catch代码快对应于try代码块。
  4. finally代码块则不管是否有异常都会执行。finally块与trycatch块对应。

1.1try

每个try都必须与一个catch or finally代码块对应, 要不然就会报语法错误。以下代码可以证明:

try {
  throw new Error('Error while executing the code');
}
Uncaught SyntaxError: Missing catch or finally after try

1.2try..catch

使用trycatch是比较优雅的方式,catch将会处理try代码块抛出的错误。

try {
  throw new Error('Error while executing the code');
} catch (err) {
  console.error(err.message);
}
➤ ⓧ Error while executing the code

1.2.1try..catch 和无效代码

try..catch 无法捕获无效代码引起的异常,如下代码,try代码块里面的有语法错误的代码引起的异常无法被catch所捕获

try {
  ~!$%^&*
} catch(err) {
  console.log("code execution will never reach here");
}
➤ ⓧ Uncaught SyntaxError: Invalid or unexpected token

1.2.2try..catch 和异步代码

类似于上面,try..catch没有办法捕获由随后执行的异步代码引起的异常。

try {
  setTimeout(function() {
    noSuchVariable;   // undefined variable
  }, 1000);
} catch (err) {
  console.log("code execution will never reach here");
}

1秒后未捕获的异常将会抛出

➤ ⓧ Uncaught ReferenceError: noSuchVariable is not definedn

优雅的处理方式是,把try..catch放到异步代码内来处理异常。

setTimeout(function() {
  try {
    noSuchVariable;
  } catch(err) {
    console.log("error is caught here!");
  }
}, 1000);

1.2.3 嵌套的 try..catch

我们可以使用嵌套的try..catch来向上抛异常。

try {
  try {
    throw new Error('Error while executing the inner code');
  } catch (err) {
    throw err;
  }
} catch (err) {
  console.log("Error caught by outer block:");
  console.error(err.message);
}
Error caught by outer block:
➤ ⓧ Error while executing the code

1.3try..finally语句

不推荐, 只使用try..finally,而不使用catch ,来看看下面的代码会导致什么问题。

try {
  throw new Error('Error while executing the code');
} finally {
  console.log('finally');
}
finally
➤ ⓧ Uncaught Error: Error while executing the code

我们至少需要注意两件事情:

  1. 即使try抛出了异常,finally块的代码也会执行
  2. 因为 catch块抛出的异常没有优雅的被捕获,将会导致 Uncaught Error

1.4try..catch..finally

建议将 try 与 catch 块和可选的 finally 块一起使用。

try {
  console.log("Start of try block");
  throw new Error('Error while executing the code');
  console.log("End of try block -- never reached");
} catch (err) {
  console.error(err.message);
} finally {
  console.log('Finally block always run');
}
console.log("Code execution outside try-catch-finally block continue..");
Start of try block
➤ ⓧ Error while executing the code
Finally block always run
Code execution outside try-catch-finally block continue..

这里我们需要看到三点:

  1. try 块抛出异常后的代码不会被执行。
  2. 这次异常被catch优雅的处理了
  3. 即使在 try 块抛出异常后, finally块也会执行。

finally 块通常用于清理资源或关闭流,如下所示:

try {
  openFile(file);
  readFile(file);
} catch (err) {
  console.error(err.message);
} finally {
  closeFile(file);
}

1.5throw

throw 语句用于抛出异常。

throw <expression>
// throw primitives and functions
throw "Error404";
throw 42;
throw true;
throw {toString: function() { return "I'm an object!"; } };

// throw error object
throw new Error('Error while executing the code');
throw new SyntaxError('Something is wrong with the syntax');
throw new ReferenceError('Oops..Wrong reference');

// throw custom error object
function ValidationError(message) {
  this.message = message;
  this.name = 'ValidationError';
}
throw new ValidationError('Value too high');

2.异步代码中的错误处理

建议对异步代码(如API调用)使用 Promises 和 async await,因为它们提供了对错误处理方法。

2.1then..catch 和 Promises

我们可以使用then() 和 catch()来处理Promise链中的异常。如下代码:

Promise.resolve(1)
  .then(res => {
      console.log(res);  // 打印'1'

      throw new Error('something went wrong');  // throw error

      return Promise.resolve(2);  // 这里不会执行
  })
  .then(res => {
      //这里的代码不会被执行,因为上个块有异常抛出导致promise没有resolved
      console.log(res);    
  })
  .catch(err => {
      console.error(err.message);  // 打印 'something went wrong'
      return Promise.resolve(3);
  })
  .then(res => {
      console.log(res);  // 打印 '3'
  })
  .catch(err => {
      //代码不会被执行因为promise已经在上个块resoved了
      console.error(err);
  })

来看一个更实际的例子,这里使用 fetch 调用 API,它返回一个 promise 对象。 我们使用 catch 块优雅地处理 API错误。

function handleErrors(response) {
    if (!response.ok) {
        throw Error(response.statusText);
    }
    return response;
}

fetch("http://httpstat.us/500")
    .then(handleErrors)
    .then(response => console.log("ok"))
    .catch(error => console.log("Caught", error));
Caught Error: Internal Server Error
    at handleErrors (<anonymous>:3:15)

2.2try..catch 和 async await

使用async await处理异步代码,这时使用try..catch将会变得非常简单,如下所示:

(async function() {
    try {
        await fetch("http://httpstat.us/500");
    } catch (err) {
        console.error(err.message);
    }
})();

再来看一个使用fetch调用api,这将返回一个promise对象,通过使用try..catch我们可以很优雅的处理api异常。

function handleErrors(response) {
    if (!response.ok) {
        throw Error(response.statusText);
    }
}

(async function() {
    try {
      let response = await fetch("http://httpstat.us/500");
      handleErrors(response);
      let data = await response.json();
      return data;
    } catch (error) {
        console.log("Caught", error)
    }
})();
Caught Error: Internal Server Error
    at handleErrors (<anonymous>:3:15)
    at <anonymous>:11:7