簡體   English   中英

了解NodeJS中的異步和承諾

[英]Understand Asynchronous and Promise in NodeJS

在過去的6個月中,我一直在使用NodeJS進行編碼,但是我仍然對異步和Promise概念沒有清晰的了解。 現在,我將使用Mongoose從MongoDB中獲取一條記錄,這可能會使branchId在每次迭代中執行一個簡單的for循環,並執行一個異步的MongoDB操作(因為MongoDB / Mongoose操作是有希望的)。 如您所知,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 });
    });
};

您正在以異步方式使用異步(在您的情況下為Promises)代碼。

這是一個異步調用:

  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值的解析,並且將在沒有Promise包裝器的情況下返回該值,而只是返回原始值,否則將收到引發的錯誤,從而允許以同步方式捕獲它。基本try catch

您可以在此處閱讀有關異步等待的更多信息

希望這可以澄清一點。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM