使用异步过滤和映射 Promise

Posted

技术标签:

【中文标题】使用异步过滤和映射 Promise【英文标题】:Filter and map Promise with async 【发布时间】:2020-06-16 22:01:36 【问题描述】:

我得到了由 sequelize.findAndCountAll() 返回的 promise 对象,我想在映射之前过滤对象。

这是我的代码:

async function getCountTask(id)
    return await Tasks.count(
        where: 
            studentID: id,
            status: 'Done',
            grade: ['A', 'B']
        
    );


let totalStudent = [];

await Promise.all(
    listStudent.filter(async (f) => 
        const count = await getCountTask(f.id); 
        if(count <= 3)
            return false;
        
        return true;
    ).map(async (e) => 
        let obj = ;
            obj.id = e.id;
            obj.age = e.age;
            obj.status = 'Great';
            totalStudent.push(obj);
    )
)

我的期望 listStudent 包含 5 个数据,但过滤后它只会包含 3 个数据,因为另外 2 个没有通过条件。因此,对于最终结果,totalStudent 包含 3 个数据。

但我从上面的代码中得到的是,totalStudent 的数据与 listStudent 完全相同。 这是因为它先处理地图然后过滤,所以地图处理尚未过滤的数据。

如何让它先成为过滤器,然后再映射数据。

提前致谢。

【问题讨论】:

您不是在使用.map 进行映射操作,而是在进行简单的迭代。但我很困惑——listStudent 是什么?它是一系列承诺吗?一个承诺?如果是这样,您必须先await(或使用 Promise API)或使用Promise.all,然后才能过滤/映射。如果listStudent 不是一个承诺,我不确定这段代码在做什么。过滤和映射实际上不适用于 async 函数。 listStudent 是一个 promise 对象,它是从 sequelize 返回的。我在上面做了await Promise.all()。或者你的意思是像const lStudent = await Promise.all(listStudent); 然后映射lStudent?它也不起作用 filter(async (f) 不会等待,实际上您的过滤器将始终返回一个承诺,并且承诺始终为真.. await Promise.all 将等待从Promise.all 返回的承诺。如果listStudent 本身就是一个promise,那么你尝试使用promise 就好像它已经解决了 并将它传递给Promise.all。我建议查看 Promise 和异步代码。 @VLAZ 谢谢你的知识,是的,我想我应该这样做。 【参考方案1】:

过滤/映射等,不支持async的使用方式。

例如。 filter 期望一个真或假,你返回的承诺async 函数始终是一个承诺。

await Promise.all(
    listStudent.filter(async (f) =>   <<<<---- This makes filter always return true
        const count = await getCountTask(f.id); 

查看您的代码,一个简单的解决方案就是删除过滤器,然后使用 map..

例如..

await Promise.all(
    listStudent.map(async (f) => 
        const count = await getCountTask(f.id); 
        if (count <= 3) return; //don't want this one.       
        let obj = ;
        obj.id = f.id;
        obj.age = f.age;
        obj.status = 'Great';
        totalStudent.push(obj);
    )
)

由于Promise.all返回一个数组,你也可以避免在totalStudent上使用push,。

例如。

totalStudent = await Promise.all(
    listStudent.map(async (f) => 
        const count = await getCountTask(f.id); 
        if (count <= 3) return; //don't want this one.       
        let obj = ;
        obj.id = f.id;
        obj.age = f.age;
        obj.status = 'Great';
        return obj;
    )
)

上面的好处是还维护了退货单。

【讨论】:

另外更好的是进行映射操作,而不是滥用.map进行简单的迭代。 return obj 最后而不是推送。如果您只是进行迭代,那么forEach 或使用循环。 是的,上面的代码确实有效。谢谢,所以我知道我上面的编码方式不起作用。谢谢你:D @VLAZ 在这种情况下它实际上很方便,因为 promise 被推送到 Promise.all ,但是是的,return obj 会很好,只需将 Promise.all 的结果分配给 totalStudent。 @VLAZ 已更新,因此 map 正在做一些更有意义的事情,而不是将承诺传递给 Promise.all 我不会称它为“方便” - 它仍然是一种误用。 arr = await Promise.all(listStudends.map(getCountTask)); arr.filter().map() 没有误导。话虽如此,listStudends 应该是一个 Promise,而不是一个数组,所以我仍然对实际发生的事情感到困惑。

以上是关于使用异步过滤和映射 Promise的主要内容,如果未能解决你的问题,请参考以下文章

聊一聊Promise

异步总结

# Promise的简单理解和基本使用

# Promise的简单理解和基本使用

javascript 循环中调用异步的同步需求

Web | ES6的异步编程