在承诺中使用 Array.push 时,数组保持为空

Posted

技术标签:

【中文标题】在承诺中使用 Array.push 时,数组保持为空【英文标题】:Array stays empty when using Array.push in promise 【发布时间】:2019-07-28 20:31:06 【问题描述】:

我正在尝试从特定文件中收集所有文档,并将它们转换为具有内容和文件名属性的 Sendgrid 附件对象。

第 1 步和第 2 步正在运行。在第 2 步之后,我有一个对象数组,其中包含一些文档数据,例如文件名、类型、存储 URL 等。

在第 3 步中,我想根据存储 URL 获取实际文件并创建一个 Sendgrid 附件对象。它需要contentfilename 作为属性。

但是,使用我当前的代码,当我记录变量时,附件数组保持为空。

我的代码:

export const onStatusChanged = functions.database.ref(`files/fileID/general/status`).onUpdate(async (change, context) => 
    const prevStatus = change.before.val();
    const currentStatus = change.after.val();

    if (prevStatus === currentStatus) return null;

    if (currentStatus === 'email') 
        // 1. Get file data
        const snapshot = await change.after.ref.parent.parent.once('value');
        const file = snapshot.val();

        // 2. Get documents
        const documents = getDocuments(file.documents);
        console.log(documents);

        // 3. Create attachments
        const attachments = [];

        documents.forEach(document => 
            axios.get(document.url,  responseType: 'arraybuffer' ).then(image => 
                attachments.push( content: Buffer.from(image.data).toString('base64'), filename: document.name );
            ).catch(error => 
                console.log(error)
            )
        );

        console.log(attachments) // []

        // 4. Create email object

        // 5. Send email
    

    return null;
)

我认为通过使用承诺我的代码是同步的?

编辑:首先我有这个代码

    // 3. Create attachments
    const attachments = documents.map(document => 
        const image = await axios.get(document.url,  responseType: 'arraybuffer' );
        return attachments.push( content: Buffer.from(image.data).toString('base64'), filename: document.name );
    )

    console.log(attachments) // [ Promise  <pending>  ]

【问题讨论】:

"我认为通过使用 promise 我的代码是同步的?" -> 正如您所发现的,事实并非如此。有不同的方法可以对异步代码进行序列化——我们曾经做过async.waterfall,也许现在还在做。您可能正在考虑使用await。这个新关键字使异步代码看起来同步,尽管它仍然绝对是异步的。您是否在您的forEach 中尝试过await @RayToal:首先我确实将 await 与 map() 函数一起使用。添加此代码 【参考方案1】:

将代码改回 async/await 并添加 Promise.all()。它现在按我想要的方式工作。

代码:

    const attachments = await Promise.all(documents.map(async document => 
        const image = await axios.get(document.url,  responseType: 'arraybuffer' );
        return  content: Buffer.from(image.data).toString('base64'), filename: document.name ;
    ));

【讨论】:

【参考方案2】:

我认为您误解了专门针对 Cloud Functions 使用 Promise。使用 Cloud Functions 后台触发器,您有义务在所有异步工作完全完成时返回一个解决方案,否则工作将被关闭。现在你返回 null,这是不正确的。

另外,attachements 似乎没有积累承诺。它正在积累对异步编程无用的其他对象。

在文档中阅读更多信息:https://firebase.google.com/docs/functions/terminate-functions

【讨论】:

我会检查文档,然后再回来。谢谢你的网址。 将代码更改为 async/await 并将循环包装在 Promise.all() 中。在 if 条件结束时(第 5 步),我将在发送实际电子邮件时返回一个承诺。

以上是关于在承诺中使用 Array.push 时,数组保持为空的主要内容,如果未能解决你的问题,请参考以下文章

POST 后 MongoDB 数组保持为空

Array.push(Object) 插入对象数组

为 array.push() 中的对象命名 [重复]

es5 - array - push

如何将链式 Promise 与数组的 for 循环一起使用?

Javascript 使数组中的新元素在 array.push 之后出现在不同的索引中(不像预期的那样在 array.length 索引中)