异常捕获

ES5 中的传统作法

假设代码块执行抛出错误 fail,那么捕获该错误的写法为:异步

try {
  // 代码块执行,并抛出 fail 错误
  throw new Error('fail');
} catch (e) {
  console.log(e);
}

定时器

咱们先来针对上面的代码改写一下,加入一个定时器。async

try {
  setTimeout(()=>{
    throw new Error('fail');
    // Uncaught Error: fail
     }, 1000);
} catch (e) {
  console.log(e);
}

像这样,将 try/catch 扔在定时器的外面,是没法捕获到内部的错误的。函数

正确的作法应该是:code

setTimeout(()=>{
  try{
    throw new Error('fail');
  } catch (e) {
    console.log(e);
  }
},1000);

Promise

function doSomething() {
  return new Promise((resolve, reject) => {
    // 同步代码中的 throw 能够被捕捉到
    throw new Error('fail');
  });
}

doSomething()
  .then((x) => {
    console.log('success:', x);
  })
  .catch((err) => {
    console.log('fail:', err);
  });

这样写是没有问题的,错误可以被捕获到。但只要稍微修改一下,可能就出现问题了。好比:同步

function doSomething() {
  return new Promise((resolve, reject) => {
    // 异步代码中的 throw 不能被 Promise 的 catch 捕捉到
    setTimeout(() => {
      throw new Error("fail");
    }, 1000);
  });
}

doSomething()
  .then((x) => {
    console.log('success:', x);
  })
  .catch((err) => {
    console.log('fail:', err);
  });

这里抛出但错误将不能被捕获。因此,在 Promise 中,咱们通常经过 reject 来抛出错误。it

function doSomething(x) {
  return new Promise((resolve, reject) => reject(x));
}

doSomething('fail').then((x) => {
  console.log('success:', x);
}).catch((err) => {
  console.log('fail:', err);
});
// fail: fail

另外,还有一个比较有意思的细节,在 catch 以后继续添加 .then 会被继续执行。io

function doSomething(x) {
  return new Promise((resolve, reject) => reject(x));
}

doSomething('fail').then((x) => {
  console.log('success:', x);
}).catch((err) => {
  console.log('fail:', err);
  // 这里能够写 return 给下面的方法继续执行
}).then((x) => {
  console.log('continue:', x);
});
// fail: fail
// continue: undefined

Async/Await

本质上来说, Async/Await 是经过 Promise 实现,因此基本跟上面 Promise 所讲的差很少。console

能够在 await 方法外嵌套 try/catch,相似这样:function

function doSomething(x) {
  return new Promise((resolve, reject) => reject(x));
}

(async () => {
  try {
    const result = await doSomething('fail');
    console.log('success:', result);
    // return 返回
  } catch (err) {
    console.log('fail:', err);
    // return 返回
  }
})();
// fail: fail

但这里就有一个问题,好比函数须要有返回,那么返回的语句就须要写两次,正常但时候返回结果,错误的时候,返回一个 throw new Error() 或者其余的。有一个小的窍门,能够这样写:class

function doSomething(x) {
  return new Promise((resolve, reject) => reject(x));
}

(async () => {
  const result = await doSomething('fail').catch((err) => {
    console.log('fail:', err);
    return 0; // 默认值
  });
  console.log('success:', result);
})();
// fail: fail
// success: 0

在错误捕获到以后,从新分配一个默认值,让代码继续运行。