一、结论
先说结论,对于通过 new Promise 等创建的 promise 实例,一定要用 reject 处理报错信息,可以通过这里写入详细的出错信息以及出错的来源,以便调用方能够跟踪错误的信息。(即下面测试案例中的方案二)。而且 reject 的处理,调用方是会正常收到错误提示的,也会阻断后续的脚本操作。
而对于 async 和 await 创建的 promise 因为没有 reject 处理的入口,所以比较适合两种处理方案:1、不主动捕捉错误,让出错的信息直接抛到调用方。2、有需要特殊处理错误的,则在 catch 里处理完,对有需要往上抛错误的场景则需要再通过 throw 的方式向调用方传递错误信息。
另外,由于 async 和 await 无法直接处理 resolve 与 reject ,因此,如果是编写一个公用的方法,不建议直接用 async 去编写,最好是自定义一个 promise ,然后将 async 包裹在这里面,这样才能够处理 resolve 与 reject。比如下方示例:

   // 用 setTimeout 模拟一个异步操作
function timeoutPromise(content, time, func) {
  return new Promise((resolve, reject) => {
    try {
      func(); // 用来触发错误
      setTimeout(() => {
        console.log(content, time);
        resolve && resolve(content);
      }, time);
      console.log(content, '再做点啥');
    } catch (error) {
      reject({
        source: `timeoutPromise ${content}`,
        error: error
      })
    }
  });
}

// 公用方法建议采用这种
function asyncFunc() {
  return new Promise(async (resolve, reject) => {
    await timeoutPromise('异步1转同步', 50, () => {});

    return await Promise.all([
      timeoutPromise('异步2', 100, () => {}),
      timeoutPromise('异步3 会出错的这一步', 200),
      timeoutPromise('异步4', 300, () => {}),
    ]).then((result) => {
      resolve(result);
    }).catch((error) => {
      reject({
        source: `${error.source} => asyncFunc`,
        error: error.error
      });
    });
  });
}

// 这种方式只适合作为最后执行的流程,或非公用的方法
async function asyncFunc() {
  await timeoutPromise('异步1转同步', 50, () => {});

  return await Promise.all([
    await timeoutPromise('异步2', 100, () => {}),
    await timeoutPromise('异步3 会出错的这一步', 200),
    await timeoutPromise('异步4', 300, () => {}),
  ]);
}

二、测试案例
1、html

    <button id="btn">测试 async 函数的异常处理</button>
  <script src="./async.js"></script>
  <script>
    document.querySelector('#btn').addEventListener('click', start);
  </script>

2、js

    /**
* 方案一:每一层都不捕捉错误
* 最底层会直接往上抛出错误
* 中间的 promise.all 除了出错的异步不能正常执行外,其他的异步都会全部执行完毕。同时也会往上抛出错误
* 最上层的 start 也会从出错的异步那里停止执行,同时直接向浏览器抛出错误
* 这种方式虽然最上层能够捕捉到错误,但由于不知是哪一层出的错,所以不好跟踪错误
*/

// // 用 setTimeout 模拟一个异步操作
// function timeoutPromise(content, time, func) {
//   return new Promise((resolve, reject) => {
//     func(); // 用来触发错误
//     setTimeout(() => {
//       console.log(content, time);
//       resolve && resolve(content);
//     }, time);
//     console.log(content, '再做点啥');
//   });
// }

// async function asyncFunc() {
//   await timeoutPromise('异步1转同步', 50, () => {});

//   return Promise.all([
//     timeoutPromise('异步2', 100, () => {}),
//     timeoutPromise('异步3 会出错的这一步', 200),
//     timeoutPromise('异步4', 300, () => {}),
//   ]);
// }

// async function start() {
//   console.log('start');
//   await asyncFunc();
//   console.log('end');
// }

/** 方案二:最底层采用 reject 处理错误
 * 与方案一一样每一层都会抛出错误,但不同的地方是这种方式可以在最底层的错误信息里加上来源,从而提高错误处理的效率
*/

// 用 setTimeout 模拟一个异步操作
// function timeoutPromise(content, time, func) {
//   return new Promise((resolve, reject) => {
//     try {
//       func(); // 用来触发错误
//       setTimeout(() => {
//         console.log(content, time);
//         resolve && resolve(content);
//       }, time);
//       console.log(content, '再做点啥');
//     } catch (error) {
//       reject({
//         source: `timeoutPromise ${content}`,
//         error: error
//       })
//     }
//   });
// }

// async function asyncFunc() {
//   await timeoutPromise('异步1转同步', 50, () => {});

//   return Promise.all([
//     timeoutPromise('异步2', 100, () => {}),
//     timeoutPromise('异步3 会出错的这一步', 200),
//     timeoutPromise('异步4', 300, () => {}),
//   ]);
// }

// async function start() {
//   console.log('start');
//   await asyncFunc();
//   console.log('end');
// }

/** 方案三:最底层采用 reject 处理错误,中间层采用了 try catch 的方式
 * 这种方案的特点是:timeoutPromise 会抛出错误给 asyncFunc,但 asyncFunc 不会抛出错误给 start。
 * 因此 start 会将后面的脚本都执行完。
 * 如果希望出错的时候不要出现
*/

// // 用 setTimeout 模拟一个异步操作
// function timeoutPromise(content, time, func) {
//   return new Promise((resolve, reject) => {
//     try {
//       func(); // 用来触发错误
//       setTimeout(() => {
//         console.log(content, time);
//         resolve && resolve(content);
//       }, time);
//       console.log(content, '再做点啥');
//     } catch (error) {
//       reject({
//         source: `timeoutPromise ${content}`,
//         error: error
//       })
//     }
//   });
// }

// async function asyncFunc() {
//   try {
//     await timeoutPromise('异步1转同步', 50, () => {});

//     Promise.all([
//       timeoutPromise('异步2', 100, () => {}),
//       timeoutPromise('异步3 会出错的这一步', 200),
//       timeoutPromise('异步4', 300, () => {}),
//     ]);
//   } catch (error) {
//     console.log('asyncFunc 捕捉并处理了错误', error);
//   }
// }

// async function start() {
//   console.log('方案三start');
//   await asyncFunc();
//   console.log('end');
// }

/** 方案四:timeoutPromise 和 asyncFunc 都用 reject 处理错误
 * 与方案三一样,asyncFunc 不会抛出错误,导致 start 无法及时捕捉到错误
*/

// // 用 setTimeout 模拟一个异步操作
// function timeoutPromise(content, time, func) {
//   return new Promise((resolve, reject) => {
//     try {
//       func(); // 用来触发错误
//       setTimeout(() => {
//         console.log(content, time);
//         resolve && resolve(content);
//       }, time);
//       console.log(content, '再做点啥');
//     } catch (error) {
//       reject({
//         source: `timeoutPromise ${content}`,
//         error: error
//       })
//     }
//   });
// }

// async function asyncFunc() {
//   await timeoutPromise('异步1转同步', 50, () => {});

//   return Promise.all([
//     timeoutPromise('异步2', 100, () => {}),
//     timeoutPromise('异步3 会出错的这一步', 200),
//     timeoutPromise('异步4', 300, () => {}),
//   ]).catch((error) => {
//     console.log(error);
//   });
// }

// async function start() {
//   console.log('方案四start');
//   await asyncFunc();
//   console.log('end');
// }

/** 方案五:timeoutPromise 用 reject 处理错误,asyncFunc 用 catch 重新抛出错误
 * 这个方案其实跟方案二类似。如果在 asyncFunc 有需要对底层的错误进行处理的,则可以加这一层,否则用方案二即可。
*/

// 用 setTimeout 模拟一个异步操作
function timeoutPromise(content, time, func) {
  return new Promise((resolve, reject) => {
    try {
      func(); // 用来触发错误
      setTimeout(() => {
        console.log(content, time);
        resolve && resolve(content);
      }, time);
      console.log(content, '再做点啥');
    } catch (error) {
      reject({
        source: `timeoutPromise ${content}`,
        error: error
      })
    }
  });
}

async function asyncFunc() {
  await timeoutPromise('异步1转同步', 50, () => {});

  return Promise.all([
    timeoutPromise('异步2', 100, () => {}),
    timeoutPromise('异步3 会出错的这一步', 200),
    timeoutPromise('异步4', 300, () => {}),
  ]).catch((error) => {
    throw error;
  });
}

async function start() {
  console.log('方案五start');
  await asyncFunc();
  console.log('end');
}

标签: none

已有 40 条评论

  1. 1 1

    555

  2. 1 1

    555

  3. 1 1

    1

  4. 1 1

    555

  5. 1 1

    1

添加新评论