# 错误处理

# 同步的错误

  1. 同步代码中的错误会中断后续同步代码的执行
  2. 后续同步代码被中断后,微任务队列中的任务会继续执行,下一个宏任务也会执行。
console.log(1);
Promise.resolve(2).then(v => {
  console.log(v);
});
new Promise(resolve => {
  setTimeout(() => {
    resolve(3);
  }, 2000);
}).then(v => {
  console.log(v);
});
console.log(4);
throw new Error('error');
console.log(5);
// 1
// 4
// 2
// Uncaught Error: error
// 3

# 异步的错误

  1. 异步代码中的错误,不影响当前同步代码的执行
  2. 异步代码中的错误,中断异步函数中后续的代码执行
console.log(1);
setTimeout(() => {
  console.log(2);
  throw new Error('error');
  console.log(3);
}, 0);
console.log(4);
setTimeout(() => {
  console.log(5);
}, 2000);
// 1
// 4
// 2
// Uncaught Error: error
// 5
console.log(1);
new Promise(() => {
  setTimeout(() => {
    Promise.resolve('2').then(v => {
      console.log(v);
    });
    console.log(7);
    throw new Error('error');
    resolve('3');
  }, 2000);
}).then(v => console.log(v));
console.log(4);
setTimeout(() => {
  console.log(5);
}, 0);
setTimeout(() => {
  console.log(6);
}, 4000);
// 1
// 4
// 5
// 7
// Uncaught Error: error
// 2
// 6

# try ... catch

try ... catch 语句只能捕获同步的错误,异步的错误捕获不到。try catch 中的错误不会影响外面的代码执行

# promise 中的错误处理

function excutor(resolve, reject) {
  // ...
}
function onResolve(resData) {
  // ...
}
function onReject(reason) {
  // ...
}
function onError(error) {
  // ...
}
new Promise(excutor).then(onResolve, onReject).catch(onError);
  1. 在 excutor 中的同步抛出的错误会中断 promise,promise 状态不会改变。

  2. 在 excutor 中的异步错误无法被捕获,promise 状态不会改变,始终是 pending

  3. 在 excutor 中,try ... catch 语句只能捕获同步的错误,异步的错误捕获不到。try catch 中的错误不会影响外面的代码执行。但是 promise 本身状态仍然不会改变,需要调用 resolve 或者 reject 改变。

  4. 在 then 中的同步错误会被之后的 catch 捕获。

  5. 在 then 中的异步错误,不会被之后的 catch 捕获,相当于 then 中 return 了 undefined,后续的 then 会继续执行。

# async-await 中的错误处理

async 函数内部 return 语句返回的值,会成为 then 方法回调函数的参数。
async 函数返回的 Promise 对象,必须等到内部所有 await 命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到 return 语句或者抛出错误。也就是说,只有 async 函数内部的异步操作执行完,才会执行 then 方法指定的回调函数。

  1. async 函数内部同步抛出错误,会导致返回的 Promise 对象变为 reject 状态。抛出的错误对象会被 catch 方法回调函数接收到。异步抛出的错误不会影响 async 函数内部同步代码的执行。
async function f() {
  setTimeout(() => {
    throw new Error('异步出错了');
  }, 1000);
  throw new Error('出错了');
}
f().then(
  v => console.log('resolve', v),
  e => console.log('reject', e)
);
//reject Error: 出错了
// 1s后
// VM5508:3 Uncaught Error: 异步出错了
async function f() {
  setTimeout(() => {
    throw new Error('异步出错了');
  }, 1000);
  return '直接return';
}
f().then(
  v => console.log('resolve', v),
  e => console.log('reject', e)
);
// resolve 直接return
// 1s后
// VM5508:3 Uncaught Error: 异步出错了
  1. 当 async 中有 await 语句时,async 函数返回的 Promise 对象,必须等到内部所有 await 命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到 return 语句或者同步地抛出错误。
function awaitTime(time = 1000) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('awaitTime done');
    }, time);
  });
}
async function awaitTest() {
  await awaitTime();
  throw new Error('出错了,后面的awiat不会执行');
  await awaitTime(2000);
  console.log('同步log一下信息');
  return 'return了结果';
}
awaitTest().then(
  v => {
    console.log('async 的then,onResolve了:', v);
  },
  e => {
    console.log('async 的then,onReject了:', e);
  }
);
// async 的then,onReject了: Error: 出错了,后面的awiat不会执行
  1. 任何一个 await 语句后面的 Promise 对象变为 reject 状态,那么整个 async 函数都会中断执行。reject 出去的信息作为 async 的 catch 方法的参数
function awaitTime(time = 1000) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('awaitTime done');
    }, time);
  });
}
async function awaitTest() {
  await awaitTime();
  await Promise.reject('promise reject');
  await awaitTime(2000);
  console.log('同步log一下信息');
  return 'return了结果';
}
awaitTest().then(
  v => {
    console.log('async 的then,onResolve了:', v);
  },
  e => {
    console.log('async 的then,onReject了:', e);
  }
);
// async 的then,onReject了: promise reject
  1. 有时,我们希望即使前一个异步操作失败,也不要中断后面的异步操作。这时可以将第一个 await 放在 try...catch 结构里面,这样不管这个异步操作是否成功,第二个 await 都会执行。另一种方法是 await 后面的 Promise 对象再跟一个 catch 方法,处理前面可能出现的错误。
function awaitTime(time = 1000) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('awaitTime done');
    }, time);
  });
}
function asyncFnReject(time = 1000) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject('promise error');
    }, time);
  });
}

async function awaitTest() {
  try {
    await asyncFnReject();
  } catch (err) {
    console.log('try catch err', err);
  }
  await asyncFnReject(2000).catch(err => {
    console.log('promise catch', err);
  });
  console.log('同步log一下信息');
  return 'return了结果';
}
awaitTest().then(
  v => {
    console.log('async 的then,onResolve了:', v);
  },
  e => {
    console.log('async 的then,onReject了:', e);
  }
);
// try catch err promise error
// promise catch promise error
// 同步log一下信息
// async 的then,onResolve了: return了结果
  1. 如果 await 后面的异步操作出错(同步地抛出错误),那么等同于 async 函数返回的 Promise 对象被 reject。
async function f() {
  await new Promise(function(resolve, reject) {
    throw new Error('出错了');
    // 异步错误将导致该promise始终为pending,async也始终为pending
    // setTimeout(() => {
    //   throw new Error('出错了');
    // }, 0);
  });
  console.log('同步 log 信息');
}

f()
  .then(v => console.log(v))
  .catch(e => console.log(e));
// Error:出错了
  1. 如果有多个 await 命令,可以统一放在 try...catch 结构中。
function awaitTime(time = 1000) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('awaitTime done');
    }, time);
  });
}
function asyncFnReject(time = 1000) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject('promise error');
    }, time);
  });
}

async function awaitTest() {
  try {
    await awaitTime();
    console.log(1);
    await asyncFnReject();
    console.log(2);
    await asyncFnReject();
    console.log(3);
  } catch (err) {
    console.log('try catch err', err);
  }

  console.log('同步log一下信息');
  return 'return了结果';
}
awaitTest().then(
  v => {
    console.log('async 的then,onResolve了:', v);
  },
  e => {
    console.log('async 的then,onReject了:', e);
  }
);
// 1
// try catch err promise error
// 同步log一下信息
// async 的then,onResolve了: return了结果