javascript - 了解 NodeJS 中的异步和 Promise

标签 javascript node.js asynchronous promise es6-promise

过去 6 个月以来,我一直在使用 NodeJS 进行编码,但我仍然对异步和 Promise 概念没有清晰的了解。现在谈到这个问题,我将使用 Mongoose 从 MongoDB 获取一条记录,该记录可能有branchId,在每次迭代中执行一个简单的 for 循环,并执行异步的 MongoDB 操作(因为 MongoDB/Mongoose 操作是 promise )。如您所知,for 循环是同步的,但我的函数在 for 循环结束之前返回值。这是怎么发生的?如果我的问题不清楚,请附上代码,将其作为评论。

const restManageChef = (params, query, body) => {
    if (query && parseBoolean(query.superChef)) {
        body = Object.assign(body, { role: 'SUPER-CHEF' });
    } else {
        body = Object.assign(body, { role: 'RES-CHEF' });
    }

    return restPUT(params, query, body).then(chef => {
        return userModel
        .findOne({ restaurantCode: chef.restaurantCode, type: 'RES-ADMIN' })
        .then(resAdminDetails => {
            log.debug({ Chef: chef }, 'Chef Details');
            if (chef.role === 'SUPER-CHEF') {
            log.debug({ BranchIds: resAdminDetails.branchIds }, 'BranchIds');
            for (let i = 0; i < resAdminDetails.branchIds.length; i) {
                log.debug({ BranchIds: resAdminDetails.branchIds[i] }, 'BranchIds');
                pushChefId(resAdminDetails.branchIds[i], chef.pkid)
                .then(restaurant => {
                    log.debug({ Restaurant: restaurant }, 'Restaurant Details');
                })
                .catch(err => {
                    log.error({ err });
                    throw err;
                });
            }
            return chef;
            } else if (chef.role === 'RES-CHEF') {
            for (let i = 0; i < resAdminDetails.branchIds.length; i++) {
                log.debug({ BranchIds: resAdminDetails.branchIds[i] }, 'BranchIds');
                pushChefId(resAdminDetails.branchIds[i], chef.pkid)
                .then(restaurant => {
                    log.debug({ Restaurant: restaurant }, 'Restaurant Details');
                })
                .catch(err => {
                    log.error({ err });
                    throw err;
                });
            }
            return chef;
            }
        });
    });
};

PushChefId函数

const pushChefId = (restaurantCode, chefId) => {
  return userModel
    .findOneAndUpdate({ restaurantCode }, { $addToSet: { chefIds: chefId } })
    .exec()
    .then(resAdmin => {
      if (!resAdmin) return Promise.reject(`No RES-ADMIN found with restaurantCode - ${restaurantCode}`);

      return storeModel.findByIdAndUpdate(restaurantCode, { $addToSet: { chefIds: chefId } }, { new: true });
    });
};

最佳答案

您正在以同步方式使用异步(在您的情况下是 Promise)代码。

这是一个异步调用:

  pushChefId(resAdminDetails.branchIds[i], chef.pkid)
    .then(restaurant => {
      log.debug({
        Restaurant: restaurant
      }, 'Restaurant Details');
    })
    .catch(err => {
      log.error({
        err
      });
      throw err;
    });

基本上,您一个接一个地触发此异步调用,并立即跳到 return 语句,而无需等待每个触发的异步调用完成。

在您的情况下,我绝对建议您考虑的一种方法是async/await,它基本上是一种编写异步代码的同步方式。

事情可能是这样的:

const decorateWithRole = (query, body) => {
  return {
    ...body,
    role: (query && parseBoolean(query.superChef) && "RES-CHEF") || "SUPER-CHEF"
  };
};

const restManageChef = async(params, query, body) => {
  const decoratedBody = decorateWithRole(query, body, parseBoolean);

  const chef = await restPUT(params, query, body);
  const resAdminDetails = await userModel.findOne({
    restaurantCode: chef.restaurantCode,
    type: "RES-ADMIN"
  });

  log.debug({
    Chef: chef
  }, "Chef Details");

  if (["SUPER-CHEF", "RES-CHEF"].includes(chef.role)) {
    log.debug({
      BranchIds: resAdminDetails.branchIds
    }, "BranchIds");
    for (let i = 0; i < resAdminDetails.branchIds.length; i++) {
      log.debug({
        BranchIds: resAdminDetails.branchIds[i]
      }, "BranchIds");
      try {
        const restaurant = await pushChefId(
          resAdminDetails.branchIds[i],
          chef.pkid
        );
        log.debug({
          Restaurant: restaurant
        }, "Restaurant Details");
      } catch (err) {
        log.error({
          err
        });
        throw err;
      }
    }
    return chef;
  }
};

const pushChefId = async(restaurantCode, chefId) => {
  const resAdmin = await userModel
    .findOneAndUpdate({
      restaurantCode
    }, {
      $addToSet: {
        chefIds: chefId
      }
    })
    .exec();

  if (!resAdmin) {
    return Promise.reject(
      `No RES-ADMIN found with restaurantCode - ${restaurantCode}`
    );
  }

  return storeModel.findByIdAndUpdate(
    restaurantCode, {
      $addToSet: {
        chefIds: chefId
      }
    }, {
      new: true
    }
  );
};

当然可以通过并行 Promise 触发等进行优化。但对于基本解释应该足够了。

关键的变化在这里:

for (let i = 0; i < resAdminDetails.branchIds.length; i++) {
      log.debug({
        BranchIds: resAdminDetails.branchIds[i]
      }, "BranchIds");
      try {
        const restaurant = await pushChefId(
          resAdminDetails.branchIds[i],
          chef.pkid
        );
        log.debug({
          Restaurant: restaurant
        }, "Restaurant Details");
      } catch (err) {
        log.error({
          err
        });
        throw err;
      }
    }
    return chef;
  }
};

async 函数上下文中的 await 关键字将等待 Promise 值的解析,并在不带 的情况下返回该值。 code>Promise 包装器,只是原始值或将接收抛出的错误,从而允许使用基本的 try catch 以同步方式捕获它。

您可以阅读有关异步等待的更多信息 here .

希望这能澄清一点。

关于javascript - 了解 NodeJS 中的异步和 Promise,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50180997/

相关文章:

javascript - 是否可以将参数拆分为 NodeJS 中的值?

node.js - node-red - 全局可用的连接池

javascript - 更改链接样式 onclick

javascript - 从javascript POST具有相同ID的多个表单

javascript - 如何链接到(大)网页的某些区域

http - 使用 com.loopj.android.http 库导致 java.lang.NoClassDefFoundError

c# - 具有异步 OnInitialized 的 Radzen DataGrid 多重选择

javascript - 为什么表单输入未定义?

javascript - 如何在 package.json 文件中指定项目 npm 注册表

c# - "awaitable"任务的严格定义是什么?