【问题标题】:array prints empty in after pushing elements into it in asynchronous function数组在异步函数中将元素推入其中后打印为空
【发布时间】:2021-05-30 19:40:12
【问题描述】:

我是异步/等待的新手。当我调用该函数时,第一个 console.log(newArr) 打印数组元素,但第二个是空数组,当我返回空数组时。有人可以帮我解决我哪里出错了。

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 
    };

【问题讨论】:

    标签: javascript asynchronous async-await promise


    【解决方案1】:

    .map() 不支持异步,因此它会在await 被击中时并行运行循环的所有迭代,开始第二次迭代)。因此,您的第二个 console.log() 正在尝试在任何回调完成之前记录 newArr,因此在他们将数据放入 newArr 之前。

    这是发生的顺序。

    1. 您拨打pmData.map()
    2. 它抓取pmData 数组中的第一个元素并调用回调
    3. 回调执行 await decryptComment(...) 导致回调暂停并立即返回一个承诺
    4. .map() 得到该承诺并将其累积为来自 .map() 的返回值
    5. .map() 然后获取pmData 数组中的下一个值并再次调用回调,即使第一个仍在等待await decryptComment()

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

    解决方案是在返回的承诺数组上使用await Promise.all(),如果您希望这些承诺并行运行,或者切换到for 循环,这将在await 被命中时暂停循环。

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

    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;
    };
    

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

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

    【讨论】:

    • 谢谢你的回答。我进行了更改,但是当我调用函数 const arr = await createPMAttributes(pmRatingId, rating);控制台.log(arr);它打印 [ undefined, undefined, undefined, undefined, undefined, undefined, undefined ] 而不是数组元素
    • @habiba - 看起来你没有我的版本中的所有更改。如果您使用的是我的第一个版本,请查看我的 return 在哪里。
    • 非常感谢!你是最棒的!它就像一个魅力! :)
    • 但是我有两个像这样的函数,一个用于 PMattributes,另一个用于 archAttributes,我想将这两个函数的评级推送到单个 arr 中,然后遍历该数组并将对象插入数据库,你知道我该怎么做吗?
    • @habiba - 我不明白。你想将你的两个函数组合成一个生成一个结果的函数,或者你有两个独立的函数,你从每个函数中得到一个结果数组,你想组合这两个数组?仅供参考,这听起来有点像一个新问题。
    猜你喜欢
    • 1970-01-01
    • 2015-08-19
    • 1970-01-01
    • 2018-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-02
    • 1970-01-01
    相关资源
    最近更新 更多