【问题标题】:Node fetch loop too slow节点获取循环太慢
【发布时间】:2021-12-23 03:10:21
【问题描述】:

我有一个 API js 文件,我用 POST 方法调用它,传入一个对象数组,每个对象都包含一个站点 url(大约 26 个对象或 url)作为正文,并使用下面的代码循环遍历这个数组(sites),检查每个对象的 url 是否返回一个 json,方法是在 url 添加 "/items.json",如果是,将 json 内容推送到另一个最终数组 siteLists,我将其作为响应发回。

问题仅在于 26 个 url,此 API 调用需要超过 5 秒 才能完成,是我做错了还是它只是 fetch 在 Node.js 中的工作方式?

const sites 内容如下:

[{label: "JonLabel", name: "Jon", url: "jonurl.com"},{...},{...}]

代码是:

export default async (req, res) => {

    if (req.method === 'POST') {
        const body = JSON.parse(req.body)

        const sites = body.list  // this content shown above
        var siteLists = []
        
        if (sites?.length > 0){
            
            var b=0, idd=0
            while (b < sites.length){

                let url = sites?.[b]?.url

                if (url){

                    let jurl = `${url}/items.json`

                    try {
                        let fUrl = await fetch(jurl)
                        let siteData = await fUrl.json()
                
                        if (siteData){
                            let items = []
                            let label = sites?.[b]?.label || ""
                            let name = sites?.[b]?.name || ""
                            let base = siteData?.items
                        
                            if(base){
                                var c = 0
                                while (c < base.length){
                                    let img = base[c].images[0].url
                                    let titl = base[c].title

                                    let obj = {
                                        url: url,
                                        img: img,
                                        title: titl
                                    }
                                    items.push(obj)
                                    c++
                                }
                                let object = {
                                    id: idd,
                                    name: name,
                                    label: label,
                                    items: items
                                }
                                
                                siteLists.push(object)
                                idd++
                            }

                        }
                        
                    }catch(err){
                        //console.log(err)
                    }
                }
            
            b++
        }

        res.send({ sites: siteLists })
    }
res.end()
}

编辑:(解决方案?) 因此,似乎带有承诺的代码如下所示,并标记为解决方案在更快的意义上工作,有趣的是它仍然需要超过 5 秒的时间来加载并且仍然抛出 Failed to load resource: the server responded with a status of 504 (Gateway Time-out) 错误,因为 Vercel,其中该应用程序托管传递给无服务器功能的最大超时时间为 5 秒,因此永远不会在响应中加载内容。在本地,我没有超时限制的地方加载速度明显更快,但令我惊讶的是,这样的查询需要这么长时间才能完成,而它应该是毫秒的问题。

【问题讨论】:

  • 您是按顺序调用这些调用,为什么不并行调用它们?

标签: javascript node.js api fetch httpresponse


【解决方案1】:

我在这里看到的最大问题是,在您循环开始下一个 fetch 请求之前,您似乎是 awaiting 完成一个 fetch,有效地运行它们串行 .如果您重写脚本以并行同时运行所有请求,您可以将每个请求按顺序推送到Promise.all,然后在结果返回时处理它们。

可以这样想——如果每个请求需要一秒钟才能完成,而您有 26 个请求,并且您等待一个请求完成后再开始下一个请求,则总共需要 26 秒。但是,如果您将它们全部一起运行,如果它们仍然只需要一秒钟来完成整个事情,则只需一秒钟。

伪代码中的一个例子--

你想改变这个:

const urls = ['url1', 'url2', 'url3'];

for (let url of urls) {
    const result = await fetch(url);
    process(result)
}

...进入这个:

const urls = ['url1', 'url2', 'url3'];

const requests = [];

for (let url of urls) {
    requests.push(fetch(url));
}

Promise.all(requests)
    .then(
        (results) => results.forEach(
            (result) => process(result)
        )
    );

【讨论】:

  • 谢谢!!我将研究这个以及如何重新调整我的代码以使用承诺
  • 这是正确的方法,但请注意,此代码的第一部分可以更简洁地编写为const requests = urls.map(fetch)
【解决方案2】:

虽然await 是一种很好的糖,但有时还是坚持使用then 会更好

export default async (req, res) => {
    if (req.method === 'POST') {
        const body = JSON.parse(req.body)
        const sites = body.list  // this content shown above
        const siteListsPromises = []
        if (sites?.length > 0){
            var b=0
            while (b < sites.length){
                let url = sites?.[b]?.url
                if (url) {
                    let jurl = `${url}/items.json`
                    // #1
                    const promise = fetch(jurl)
                        // #2
                        .then(async (fUrl) => {
                            let siteData = await fUrl.json()
                            if (siteData){
                                ...
                                return {
                                    // #3
                                    id: -1,
                                    name: name,
                                    label: label,
                                    items: items
                                }
                            }
                        })
                        // #4
                        .catch(err => {
                            // console.log(err)
                        })
                    siteListsPromises.push(promise)
                }
                b++
            }
        }
        // #5
        const siteLists = (await Promise.all(siteListsPromises))
            // #6
            .filter(el => el !== undefined)
            // #7
            .map((el, i) => ({ id: i, ...el }))
        res.send({ sites: siteLists })
    }
    res.end()
}

在 sn-p 中查找// #N cmets。

  1. 不要await 请求完成。而是遍历 sites 并一次发送所有请求
  2. json()siteDatafetchthen 之后处理。如果您对siteData 的处理更加繁重,那么这样做会更有意义,而不是在所有承诺都解决后才执行所有这些操作。
  3. 如果您(或您团队中的某个人)在理解闭包方面遇到了一些麻烦,请不要费心在循环中设置 idsiteData 元素。我不会深入探讨,但会进一步解决。
  4. 使用.catch() 而不是try{}catch(){}。因为没有await,它将无法工作。
  5. await 带有Promise.all() 的所有请求的结果
  6. 过滤掉那些siteData 是假的
  7. 最终设置id 字段。

【讨论】:

  • 非常感谢您的回复。我收到“TypeError: Cannot read properties of undefined (reading 'map')”这与第 6 点有关吗?
  • console.log(siteListsPromises) 在 #5 正确记录之前:[ Promise { }, Promise { }, ... 24 更多]
  • 1) 请说得更具体一些。不仅是错误,还有行。我已经修复了一个错误,但它不应该是您正在考虑的错误。 await Promise.all 之后的部分对我有用,所以也许这个错误在其他地方。 2)SO也不是为你调试你的代码。这是关于回答问题。只需放置一个断点或console.log,您就会发现问题所在。不过,如果您将制作一个可重现的最小示例并将其粘贴到某个沙箱中,我将能够对其进行调试。但猜测不是一个好主意。
  • 好吧,我发布的代码是一个可重现的示例,实际上是整个代码,如果您只使用我提供的虚拟内容( [{label: "JonLabel", name: "Jon", url : "jonurl.com"},{...},{...}] ) 你会看到你的代码不起作用。
  • 并对其进行调试,我可以确认您是在 #7 中使用的 .map 引发了错误,因此您在 #6 中使用的过滤器似乎无法正常工作
猜你喜欢
  • 2021-12-20
  • 2016-01-01
  • 2020-09-09
  • 2012-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-12
相关资源
最近更新 更多