NodeJS - 在 Array.map 中使用 Async/Await 和“if”语句

Posted

技术标签:

【中文标题】NodeJS - 在 Array.map 中使用 Async/Await 和“if”语句【英文标题】:NodeJS - Using Async/Await inside Array.map with 'if' statement 【发布时间】:2019-01-03 19:53:39 【问题描述】:

我有一个循环遍历对象的递归函数。它查找名称为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 数字应该按顺序触发,但我得到的是23,然后是1。结果,递归函数在异步函数完成之前运行,因此永远不会按预期替换子函数;我在1 得到了正确的结果,但它没有传递给23

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

我查看了以下 SO 帖子:

Using async/await inside for loop Best way to call an async function within map? Async/Await inside Array#map() Use async await with Array.map

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

编辑

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

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

【问题讨论】:

使用await doPopulate(data[key])。为此,您还必须回调.map async。事实上你不需要doPopulate async;你只需要回报承诺。 @ExplosionPills Object.keys(data).map(async (key) => (第2行)和await doPopulate(data[key])(第14行)似乎没有做任何事情......我不是已经用return Promise.all(promises).then((output) => 返回了承诺吗?跨度> 不,因为在您当前的迭代中 doPopulate(data[key])data[key] = output; 之前运行 @KevinB - 是的......这基本上是我的问题 - 我不确定如何正确构建我的代码,以便 doPopulate(data[key]) 等待 if 语句完成......跨度> @GarethJames 为什么不能在 promise 的 .then AND 语句中? 【参考方案1】:

只要下一个 Promise 依赖于前一个 Promise,您就必须依次等待每个 Promise。如果它们可以同时完成,您只需聚合承诺并与Promise.all 一起等待它们。顺便说一句,如果您需要使用await,则只需要async 关键字。否则没有理由创建函数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;
    );

【讨论】:

为此干杯!不太好用,但使用data[key] = await Promise.all(promises) 找出答案...【参考方案2】:

事实证明,我需要按照 @Bergi 在 cmets 中的建议返回两组承诺:

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),我还需要使流函数也异步:

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

【讨论】:

以上是关于NodeJS - 在 Array.map 中使用 Async/Await 和“if”语句的主要内容,如果未能解决你的问题,请参考以下文章

为啥我们不能在 Array.map() 中使用扩展运算符,还有啥可以替代展平数组? [复制]

如何在array.map()中返回动态对象键[重复]

Array的 map() 和 reduce()

在Array#map()中异步/等待

如何知道我正在使用的 array.map 的哪个迭代?

尝试使用函数调用返回时,array.map 给出未定义 [重复]