首页 > 解决方案 > NodeJS - 在 Array.map 中使用 Async/Await 和“if”语句

问题描述

我有一个循环遍历对象的递归函数。它查找具有名称的键subcomponent(始终是一个数组)并在每个subcomponent子节点上执行一个异步函数,其输出用于替换子节点的数据。

在下面的示例中populateSubcomponent()是异步函数。

代码:

async function doPopulate(data) {
    Object.keys(data).map((key) => {
        if (typeof data[key] === 'object') { // We want to do a recursive check on the data so are interested in any objects (arrays) at this point
            if (key === 'subcomponent') { // If we have an object (array) with key `subcomponent` we want to populate each of its children
                const promises = data[key].map(subcomponent => populateSubcomponent(subcomponent));
                return Promise.all(promises).then((output) => {
                    data[key] = output;
                    console.log('1');
                });
            }
            doPopulate(data[key]); // Check recursively
            console.log('2');
        }
        console.log('3');
        return data;
    });
}
doPopulate(data);

我的期望是每个console.log数字都应该按顺序触发,但我却得到2, 3, then 1。结果,递归函数在异步函数完成之前运行,因此永远不会按预期替换子函数;我得到了正确的结果,1但它没有传递给2or 3

如何最好地将递归doPopulate()调用与if语句结合起来?

我查看了以下 SO 帖子:

但我无法将任何答案与我自己的问题联系起来,主要是因为我if在递归函数中有一个语句,我不确定如何在异步内容的上下文中处理它。

编辑

感谢大家的评论,我想出了以下内容:

async function doPopulate(data) {
    const promisesOuter = Object.keys(data).map(async (key) => {
        if (typeof data[key] === 'object') { // We want to do a recursive check on the data so are interested in any objects (arrays) at this point
            if (key === 'subcomponent') { // If we have an object (array) with key `subcomponent` we want to populate each of its children
                const promisesInner = data[key].map(subcomponent => populateSubcomponent(subcomponent));
                data[key] = await Promise.all(promisesInner);
            }
            await doPopulate(data[key]); // Check recursively
        }
        return data;
    });
    return Promise.all(promisesOuter);
}
return doPopulate(data);

由于这一切都发生在 NodeJS 流中(使用through2),我还需要使流函数异步:

const through = require('through2');

return through.obj(async (file, enc, done) => {
    const data = JSON.parse(file.contents.toString());
    await doPopulate(data);
    file.contents = Buffer.from(JSON.stringify(data));
    return done(null, file);
});

标签: javascriptarraysnode.jsasynchronousasync-await

解决方案


只要下一个 Promise 依赖于前一个 Promise,您就必须依次等待每个 Promise。如果它们可以同时完成,您只需聚合 Promise 并将它们与Promise.all. 顺便说一句,async如果您需要使用await. 否则没有理由做这个函数async

如果您想按顺序等待每个承诺,您可以像这样重写它:

function doPopulate(data) {
    return Object.keys(data).map(async (key) => {
        if (typeof data[key] === 'object') { // We want to do a recursive check on the data so are interested in any objects (arrays) at this point
            if (key === 'subcomponent') { // If we have an object (array) with key `subcomponent` we want to populate each of its children
                const promises = data[key].map((subcomponent) =>
                    populateSubcomponent(subcomponent)
                );
                data[key] = await Promise.all(promises);
                console.log('1');
            }
            await doPopulate(data[key]); // Check recursively
            console.log('2');
        }
        console.log('3');
        return data;
    });
}

推荐阅读