在异步映射中推送到数组

Pushing to array in asynchonous map

我知道这个问题已经有人问过,但我无法正确回答。尝试使用承诺但无济于事。当控制台记录 req.user items 仍然是一个空数组。我知道我应该使用 promises 但我在实现它时遇到了麻烦。帮助赞赏

app.get('/cart',checkAuthenticated, async (req, res) => {
    if(req.user.cart.length > 0){
        req.user.items = []
       
        req.user.cart.map(async (item) => {
            var itemDescription = await productsModel.findOne({id: item.itemId})
            req.user.items.push(itemDescription)
        });
        
        console.log(req.user)
    }

它为空的原因是您没有等待映射中的所有异步函数完成。试试这个:

await Promise.all(req.user.cart.map(async (item) => {
  var itemDescription = await productsModel.findOne({id: item.itemId})
  req.user.items.push(itemDescription)
}));

请注意,@jfriend00 已评论此实现将不保证 req.user.items 中项目的顺序。

因为您已经在使用地图,所以执行以下操作会更简单,而且还能保证项目的顺序:

req.user.items = await Promise.all(req.user.cart.map(async (item) => {
  var itemDescription = await productsModel.findOne({id: item.itemId})
  return itemDescription;
}));

.map() 不是 promise-aware。它不会注意您的 async 回调函数 returns 的承诺。因此,一旦您点击 await productsModel.findOne(...)async 函数 returns 一个未实现的承诺,并且 .map() 前进到循环的下一次迭代。

有很多不同的方法可以解决这个问题。如果你想使用.map(),那么你需要注意你的回调是这样返回的承诺:

app.get('/cart', checkAuthenticated, async (req, res) => {
    if (req.user.cart.length > 0) {

        req.user.items = await Promise.all(req.user.cart.map((item) => {
            return productsModel.findOne({ id: item.itemId });
        }));

        console.log(req.user)
    }
});

以上实现将尝试运行所有数据库并行查找。


一个更简单的实现只是使用一个简单的 for 循环和 运行 数据库一次查找一个:

app.get('/cart', checkAuthenticated, async (req, res) => {
    if (req.user.cart.length > 0) {
        req.user.items = [];

        for (let item of req.user.cart) {
            req.user.items.push(await productsModel.findOne({ id: item.itemId }));
        }

        console.log(req.user)
    }
});

在您的示例中,数组仍然是空的,因为 map 函数中的回调是异步工作的,因此您需要等待代码完成。因为 map 函数 returns promises 数组,它们都需要使用 Promise.all:

来解决
app.get('/cart', checkAuthenticated, async (req, res) => {
    if (req.user.cart.length > 0) {
        req.user.items = []

        const promises = req.user.cart.map(async (item) => {
            var itemDescription = await productsModel.findOne({ id: item.itemId })
            req.user.items.push(itemDescription)
        });

        await Promise.all(promises);

        console.log(req.user)
    }
});

否则,你可以用for循环替换map函数:

app.get('/cart', checkAuthenticated, async (req, res) => {
    if (req.user.cart.length > 0) {
        req.user.items = []

        for (const item of req.user.cart) {
            var itemDescription = await productsModel.findOne({ id: item.itemId })
            req.user.items.push(itemDescription)
        }

        console.log(req.user)
    }
});