Sequelize 中使用 Promise.all 时如何处理异常?

在使用 Sequelize 进行数据库操作时,我们经常会使用 Promise.all 来同时执行多个异步操作,以提高效率,但是如果其中有一个操作出现了异常,整个 Promise.all 将会直接 reject,导致其他操作也无法执行。因此,对于 Sequelize 中使用 Promise.all 的异常处理十分重要。本文将介绍如何在 Sequelize 中使用 Promise.all 时正确地处理异常,以及如何真正地提高效率和代码可靠性。

Promise.all 与异常处理

在 JavaScript 中,Promise.all 是一种常见的并行执行多个异步操作的方式。Promise.all 接收一个 Promise 数组作为参数,如果所有 Promise 都 resolved,返回一个包含所有 Promise 的 return 值的数组,如果其中任何一个 rejected,Promise.all 将直接返回一个 rejected Promise。看下面的例子:

const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);
const promise3 = Promise.reject(new Error('3'));

Promise.all([promise1, promise2, promise3])
  .then(([res1, res2, res3]) => {
    console.log(res1, res2, res3);
  })
  .catch((err) => {
    console.error(err);
  });

在这个例子中,promise1 和 promise2 都 resolved 了,但是 promise3 rejected,所以 Promise.all 直接返回了一个 rejected Promise,catch 中的 err 就是 rejected Promise 的拒因,即 Error('3')。

Sequelize 中的 Promise.all

在 Sequelize 中,我们经常使用 Promise.all 来同时执行多个数据库操作,如下所示:

const [user, task] = await Promise.all([
  UserModel.findById(1),
  TaskModel.findAll({ where: { userId: 1 } }),
]);

这段代码中,我们同时执行了查找 id 为 1 的用户和查找所有 userId 为 1 的任务两个操作。如果一个操作出现了异常,整个 Promise.all 就会中止并返回一个 rejected Promise。正常情况下,我们需要使用 try-catch 或 Promise.catch 来处理异常。具体来说,我们可以这样来编写:

try {
  const [user, task] = await Promise.all([
    UserModel.findById(1),
    TaskModel.findAll({ where: { userId: 1 } }),
  ]);
} catch (err) {
  console.error(err);
}

或者这样:

Promise.all([
  UserModel.findById(1),
  TaskModel.findAll({ where: { userId: 1 } }),
])
  .then(([user, task]) => {
    // Do something
  })
  .catch((err) => {
    console.error(err);
  });

在这两种方式中,我们都使用了 try-catch 或 Promise.catch 来捕获异常,但是这样处理异常有一个问题:如果一个操作异常了,整个 Promise.all 就会中止,导致其他操作也无法执行。因此,我们需要对 Sequelize 中的 Promise.all 进行一些改进,使得它能够对异常进行更好的处理。

改进的方式

我们可以通过对每个操作进行封装,使其能够 individually 处理异常,即如果一些操作出现了异常,其他操作仍然可以进行。下面是一个使用这种方式的示例代码:

/**
 * 在 Sequelize 中执行多个操作,同时支持异常处理
 * @param {Array} arr     所有的操作,每个操作应当返回一个 Promise
 * @param {Boolean} all   是否等待所有操作完成后一起返回
 *
 * @returns {Promise}     如果 all=true,返回一个 Promise 对象,否则返回一个数组
 */
function sequelizeAll(arr, all = true) {
  const results = [];
  const promises = arr.map((promise) =>
    // 监听每个操作的异常
    promise.catch((err) => {
      // 将异常存入 results 数组
      results.push({ error: err });
      // 继续执行下一个操作
      return null;
    }),
  );

  return Promise.all(promises).then((data) => {
    data.forEach((item, index) => {
      const result = results[index];

      if (result) {
        // 如果该操作异常,将异常返回
        data[index] = result;
      } else {
        // 否则,将该操作的结果存入 results 数组
        results[index] = { data: item };
      }
    });

    if (all === true) {
      // 如果 all=true,返回所有操作的结果
      const error = results.find((item) => item.error);
      if (error) {
        // 如果有异常,直接返回异常
        return Promise.reject(error.error);
      } else {
        return results.map((item) => item.data);
      }
    } else {
      // 如果 all=false,只返回成功的操作结果
      return results
        .filter((item) => item.data !== null)
        .map((item) => item.data);
    }
  });
}

这个函数将所有的操作遍历一遍,并分别对每个操作进行封装。对于每个操作,我们都添加了一个 catch,它会在操作发生异常时触发,将该异常存入 results 数组,然后继续执行下一个操作。

对于所有的操作,该函数都会返回一个 Promise 数组,我们使用 Promise.all 来并行执行所有操作。执行结束后,我们遍历了 data 数组,根据 results 数组将每个操作的结果合并到一个数组中。如果某个操作出现了异常,我们将该操作的异常存入 results 数组中,否则,将该操作的结果存入 results 数组中。最终,将 results 数组返回给调用方即可。

值得注意的是,如果 all=true,我们会检查 results 数组中是否有任何一个操作异常,如果有,直接返回 rejected Promise,否则,返回操作的结果组成的数组。如果 all=false,我们只返回所有没有异常的操作结果。

使用 sequelizeAll 进行操作

使用 sequelizeAll 非常简单,只需要将一个 Promise 数组传给它即可。下面是一个示例,它通过 sequelizeAll 从数据库中获取两个表的数据,并处理异常:

sequelizeAll([
  UserModel.findById(1),
  TaskModel.findAll({ where: { userId: 1 } }),
])
  .then(([user, task]) => {
    // Do something
  })
  .catch((err) => {
    console.error(err);
  });

这段代码中,我们将两个操作作为 Promise 数组传进了 sequelizeAll 中,然后使用 then 和 catch 对其进行处理。

总结

Sequelize 中的 Promise.all 非常常用,但它的异常处理机制并不完善。我们可以通过封装每个操作,使其 individually 处理异常,从而提高代码的可靠性和效率。本文介绍了一种改进 Sequelize 中 Promise.all 异常处理的方式,并提供了代码示例。相信读完本文,你已经了解了如何在 Sequelize 中使用 Promise.all 时正确地处理异常,以及如何真正地提高效率和代码可靠性。

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65b75ec9add4f0e0fffee312