在异步函数中将元素推入数组后,数组打印为空

array prints empty in after pushing elements into it in asynchronous function

我是 async/await 的新手。当我调用函数时,第一个 console.log(newArr) 打印数组元素,但第二个是空数组,当我 return 时,数组也是空的。谁能帮我看看哪里出错了。

const prefixPM = 'PM';
    const decryptComment = (comment) => {
          const data = decrypt(comment).then(function (result) {
            const buf2 = Buffer.from(result, 'base64').toString('ascii');
            return buf2;
          });
          return data;
        };
    
const pmData = await queryInterface.sequelize.query(
      `select ram.id, ra.name from rater_attributes ra
      inner join rater_attribute_mappings ram
      on ra.id = ram.raterAttributeId
      inner join attributes a
      on a.id = ram.by
      where a.name='Project Manager'`,
      { raw: true, type: Sequelize.QueryTypes.SELECT },
    );

const createPMAttributes = (ratingId, rating) => {
      const newArr = [];
      pmData.map(async (data) => {
        const attribute = `${prefixPM}${data.name}`;

        let pmComment = rating.PMComment
          ? await decryptComment(rating.PMComment)
          : null;
        pmComment = JSON.parse(pmComment);
        newArr.push({
          ratingId: ratingId,
          raterAttributeMappingId: data.id,
          rating: rating[`${attribute}`],
          comment:
            pmComment && pmComment[`${attribute}Comment`] ? pmComment[`${attribute}Comment`] : null,
          createdAt: rating.createdAt,
          updatedAt: rating.updatedAt,
        });
      console.log(newArr) -- this prints the newArr elements
      });
      console.log(newArr); -- this prints empty arr
return newArr 
    };

.map() 不是异步感知的,因此一旦 await 被命中,它就会 运行 并行开始第二次循环的所有迭代)。因此,您的第二个 console.log() 试图在任何回调完成之前记录 newArr,因此在他们将数据放入 newArr.

之前

这是发生的顺序。

  1. 你打电话给pmData.map()
  2. 它获取 pmData 数组中的第一个元素并调用回调
  3. 回调执行 await decryptComment(...) 导致回调暂停并立即 return 承诺
  4. .map() 得到那个承诺,并从 .map()
  5. 中为 return 值累积它
  6. .map() 然后获取 pmData 数组中的下一个值并再次调用回调,即使第一个仍在等待 await decryptComment()

因此,您最终 运行 并行处理所有这些,并且您的 .map() 在任何回调完成之前完成。

解决方案是在 returned 承诺数组上使用 await Promise.all(),如果您希望它们并行 运行,或者切换到 for 循环将在 await 被点击时暂停循环。

这是一个解决方案,同时仍然 运行 并行执行所有调用。这 return 是一个解析为您的值数组的承诺。

const createPMAttributes = (ratingId, rating) => {
    return Promise.all(pmData.map(async (data) => {
        const attribute = `${prefixPM}${data.name}`;

        let pmComment = rating.PMComment ?
            await decryptComment(rating.PMComment) :
            null;
        pmComment = JSON.parse(pmComment);
        return {
            ratingId: ratingId,
            raterAttributeMappingId: data.id,
            rating: rating[`${attribute}`],
            comment: pmComment && pmComment[`${attribute}Comment`] ? pmComment[
                `${attribute}Comment`] : null,
            createdAt: rating.createdAt,
            updatedAt: rating.updatedAt,
        };
    }));
};

并且,这是一个使用 for 循环的解决方案,该循环 运行 按顺序执行操作:

const createPMAttributes = async (ratingId, rating) => {
    const newArr = [];
    for (let data of pmData) {
        const attribute = `${prefixPM}${data.name}`;

        let pmComment = rating.PMComment ?
            await decryptComment(rating.PMComment) :
            null;
        pmComment = JSON.parse(pmComment);
        newArr.push({
            ratingId: ratingId,
            raterAttributeMappingId: data.id,
            rating: rating[`${attribute}`],
            comment: pmComment && pmComment[`${attribute}Comment`] ? pmComment[
                `${attribute}Comment`] : null,
            createdAt: rating.createdAt,
            updatedAt: rating.updatedAt,
        });
    }
    return newArr;
};

这里是一般提示。仅当您真正想要使用它生成的 returned 数组时才使用 .map()。如果您要做的只是使用它来迭代数组,那么请改用 for 循环,因为它更加灵活并且可以识别 async

并且,如果您因为使用 await 而将 .map() 回调为 async,那么您将 运行 执行所有操作并行地知道它何时完成的唯一方法是在回调生成的承诺数组上使用 Promise.all() 。请记住,每个 async 函数 return 都是一个承诺 - 永远如此。因此,let resultArr = someArr.map(async => {} {}) 将始终生成一组 promises,告诉您事情何时真正完成。