【问题标题】:How to handle a long request with Express如何使用 Express 处理长请求
【发布时间】:2020-04-18 19:40:02
【问题描述】:

我正在为浏览器中触发的特定 GET 请求开发一个简单的函数。此请求的目的是对 mongodb (mongoose) 数据库进行多次查询,然后对结果执行一些计算和结构格式化以将其发送回浏览器。 唯一的问题是一切耗时太长,导致浏览器出错:

net::ERR_EMPTY_RESPONSE

举一个我试图在这里构建的部分功能的例子:

async function getPriceByMake(makes, id) { 
    return new Promise(async (resolve, reject) => {
        let pMakes = {};
        const makesArr = Object.keys(makes);
        for (let i = 0; i < makesArr.length; i++) {
            console.log('Getting the docs ... ' + Math.round(i/makesArr.length*100) + '%')
            const currMake = makesArr[i];
            pMakes[currMake] = {};
            const modelsArr = Object.keys(makes[currMake]);
            for (let j = 0; j < modelsArr.length; j++) {
                const currModel = modelsArr[j];
                await Listing.find({ catFrom: id, model: currModel }, 'year asking', (err, docs) => {
                    if (docs.length > 1) { 
                        pMakes[currMake][currModel] = [docs];
                    } else {
                        pMakes[currMake][currModel] = {};
                    }
                });
            }
        }
        resolve(pMakes);
    });
}

在这个函数中,如果我把async / await 放在外面,我会在另一端得到一个空的{}。这显然不是目标。

我在网上搜索了一下,找到了一个指向这个方案的article

浏览器

  • 发起请求
  • 显示进度
  • 显示结果

网络服务器:

  • 提交活动
  • 检查完成
  • 返回结果

BackEndApp:

  • 领取活动
  • 运行任务
  • 返回结果

我的问题如下: 如何使用 NodeJS 和 Express 做到这一点?

【问题讨论】:

    标签: node.js express mongoose async-await request


    【解决方案1】:

    在这段代码中:

            for (let j = 0; j < modelsArr.length; j++) {
                const currModel = modelsArr[j];
                await Listing.find({ catFrom: id, model: currModel }, 'year asking', (err, docs) => {
                    if (docs.length > 1) { 
                        pMakes[currMake][currModel] = [docs];
                    } else {
                        pMakes[currMake][currModel] = {};
                    }
                });
            }
    

    您的await 不起作用,因为您将回调传递给Listing.find()。当你这样做时,它不会返回一个承诺,因此 await 没有任何用处。您得到空响应,因为 await 不起作用,因此您在没有任何实际数据之前调用 resolve()

    把代码改成这样:

            for (let j = 0; j < modelsArr.length; j++) {
                const currModel = modelsArr[j];
                let docs = await Listing.find({ catFrom: id, model: currModel }, 'year asking');
                if (docs.length > 1) { 
                    pMakes[currMake][currModel] = [docs];
                } else {
                    pMakes[currMake][currModel] = {};
                }
            }
    

    然后,await 将正常工作。

    您还应该删除 return new Promise() 包装器。你不想要那个。只需创建函数async 并使用await,它就会返回一个promise。

    这是删除了不必要的 promise 包装器的函数:

    async function getPriceByMake(makes, id) { 
        let pMakes = {};
        const makesArr = Object.keys(makes);
        for (let i = 0; i < makesArr.length; i++) {
            console.log('Getting the docs ... ' + Math.round(i/makesArr.length*100) + '%')
            const currMake = makesArr[i];
            pMakes[currMake] = {};
            const modelsArr = Object.keys(makes[currMake]);
            for (let j = 0; j < modelsArr.length; j++) {
                const currModel = modelsArr[j];
                let docs = await Listing.find({ catFrom: id, model: currModel }, 'year asking');
                if (docs.length > 1) { 
                    pMakes[currMake][currModel] = [docs];
                } else {
                    pMakes[currMake][currModel] = {};
                }
            }
        }
        return pMakes;
    }
    

    然后,请记住,在调用此async 函数时,无论发送您的实际响应的代码都需要使用.then()await 以获得最终结果。


    加快此代码的最佳选择是重构您的查询或数据库结构或两者都不必执行 N * M 个单独的查询来获得最终结果。这很可能是你的缓慢来自哪里。最大的性能提升可能来自将您必须在此处运行的查询数量减少到更少。

    根据您的数据库配置和功能,它可能会加快并行运行内部循环查询的速度,如下所示:

    async function getPriceByMake(makes, id) { 
        let pMakes = {};
        const makesArr = Object.keys(makes);
        for (let i = 0; i < makesArr.length; i++) {
            console.log('Getting the docs ... ' + Math.round(i/makesArr.length*100) + '%')
            const currMake = makesArr[i];
            pMakes[currMake] = {};
            const modelsArr = Object.keys(makes[currMake]);
            await Promise.all(modelsArr.map(async currModel => {
                let docs = await Listing.find({ catFrom: id, model: currModel }, 'year asking');
                if (docs.length > 1) { 
                    pMakes[currMake][currModel] = [docs];
                } else {
                    pMakes[currMake][currModel] = {};
                }
            }));
        }
        return pMakes;
    }
    

    【讨论】:

    • 嘿@jfriend00!感谢您的回答。你的代码看起来比我的更简单/更好,我实际上试图将它实现到函数的其余部分,但现在我的后端执行得更慢。请注意,它变慢并不重要,对我来说重要的是如何在我的网络服务器/浏览器更新状态时在后端执行它?
    • @Ardzii - 嗯,http 并没有真正传达部分结果。人们这样做的一种方法是通过 webSocket 或 socket.io 连接发送部分结果,但这会使事情变得非常复杂。
    • @Ardzii - 我建议您可能需要改进使用数据库的方式,这样您就不会进行 N * M 单独的查询。我不知道您的数据库结构或您要在这里解决的真正问题是什么,但我希望有一个数据库结构和一个查询结构可以在更少的查询中获得您想要的结果。这可能是大部分时间都被占用的地方。
    • @Ardzii - 此外,根据您的数据库配置,并行执行许多请求可能会有所帮助,因为每个查询似乎独立于其他查询。您可以并行执行来自内部循环的所有查询,await 仅在下一次外部循环迭代之前执行所有查询的最终结果。
    • @Ardzii - 我添加了一个并行运行内部循环查询的代码示例(在答案的末尾)。
    猜你喜欢
    • 2022-01-20
    • 2017-02-05
    • 2020-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-29
    • 2020-10-17
    • 1970-01-01
    相关资源
    最近更新 更多