快速服务器的响应时间差异很大,异步/等待的输出顺序错误

发布于 2025-01-17 10:43:18 字数 2108 浏览 3 评论 0原文

我有一个快速服务器,我在其中调用 API 路由并循环访问从 fetch 方法返回的对象。在循环中,我调用另一个 API 并将内容推送到数组中。循环完成后,我在响应对象中返回数组。请参阅我的代码(该问题的网址已更改,并且实际上不可调用)

app.get('/', async (req, res) => {
  const userId = '123'

  const userData = await fetch(
    `https://www.exampleTourApi.com/users/${userId}/tours?type=tour_recorded&sort_field=date&sort_direction=asc&status=public`
  )

  let tours = null

  try {
    const data = await userData.json()
    tours = data.tours
  } catch (error) {
    console.log('Invalid User ID!')
    return
  }

  let tourContents = []

  await Promise.all(
    tours.map(async tour => {
      try {
        const url = `https://www.exampleTourApi.com/tours/${tour.id}.gpx`

        const gpxFile = await fetch(url)

        if (gpxFile.status !== 200) {
          console.log(gpxFile.status)
          return
        }

        console.log(`Get content for ${url}`)

        const gpxFileContent = await gpxFile.text()

        console.log(`Content length: ${gpxFileContent.length}`)

        tourContents.push(gpxFileContent)

        console.log('Pushed to array')
      } catch (error) {
        console.log(`Unexpected error ${error}`)
        return
      }
    })
  )

  console.log('All finished')

  const response = {
    code: 200,
    tours: tourContents,
  }

  return res.json(response)
})

但是,我面临两个问题:

  1. 通过前端中的 fetch 调用服务器路由时,响应时间差异很大。即使我总是调用相同的端点,它也可能只是 4 秒或一整分钟。我知道 JS 执行通常会有几毫秒的变化,但这是一个很大的差距。

2.我使用console.log来调试行为,但即使我有一个带有await的异步函数,控制台输出的顺序也是错误的。当所有请求完成时,只有“全部完成”console.log 才会正确触发。这是一个例子:

   Get content for https://www.exampleTourApi.com/tours/47742549.gpx
   Get content for https://www.exampleTourApi.com/tours/254832437.gpx
   Content length: 214545
   Pushed to array
   Get content for https://www.exampleTourApi.com/tours/233629471.gpx
   Content length: 214868
   Pushed to array
   Get content for https://www.exampleTourApi.com/tours/231785145.gpx
   Content length: 35955

你能指出我正确的方向吗?非常感谢!

I have an express server where I call an API route and loop through an object that gets returned from the fetch method. Within the loop, I'm calling another API and push the contents to an array. When the loop is finished, I return the array in a response object. See my code (the urls were changed for this question and are not actually callable)

app.get('/', async (req, res) => {
  const userId = '123'

  const userData = await fetch(
    `https://www.exampleTourApi.com/users/${userId}/tours?type=tour_recorded&sort_field=date&sort_direction=asc&status=public`
  )

  let tours = null

  try {
    const data = await userData.json()
    tours = data.tours
  } catch (error) {
    console.log('Invalid User ID!')
    return
  }

  let tourContents = []

  await Promise.all(
    tours.map(async tour => {
      try {
        const url = `https://www.exampleTourApi.com/tours/${tour.id}.gpx`

        const gpxFile = await fetch(url)

        if (gpxFile.status !== 200) {
          console.log(gpxFile.status)
          return
        }

        console.log(`Get content for ${url}`)

        const gpxFileContent = await gpxFile.text()

        console.log(`Content length: ${gpxFileContent.length}`)

        tourContents.push(gpxFileContent)

        console.log('Pushed to array')
      } catch (error) {
        console.log(`Unexpected error ${error}`)
        return
      }
    })
  )

  console.log('All finished')

  const response = {
    code: 200,
    tours: tourContents,
  }

  return res.json(response)
})

However, I face two problems:

  1. The response times differ heavily when calling the server route via fetch in the frontend. It can be just 4 seconds or a whole minute, even though I'm always calling the same endpoints. I know there can be some variation in the JS execution of a few milliseconds normally, but this is a big gap.

Response times in Chrome

2. I used console.log's to debug the behaviour, but even though I have an async function with await, the console out is in wrong order. Only the 'All finished' console.log fires correctly when all requests are done. Here's an example:

   Get content for https://www.exampleTourApi.com/tours/47742549.gpx
   Get content for https://www.exampleTourApi.com/tours/254832437.gpx
   Content length: 214545
   Pushed to array
   Get content for https://www.exampleTourApi.com/tours/233629471.gpx
   Content length: 214868
   Pushed to array
   Get content for https://www.exampleTourApi.com/tours/231785145.gpx
   Content length: 35955

Can you point me in the right direction? Many thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

情泪▽动烟 2025-01-24 10:43:19

关于输出顺序。您正在并行执行所有fetch(url),因此完成订单取决于请求首先响应。这就是为什么您不应该手动将结果推入数组。相反,您应该返回结果。 Promise.all()确保所有结果的放置与传递的数组的顺序相同:

tourContents.push(gpxFileContent) // delete this line

将代码更改为此:

    const url = `https://www.exampleTourApi.com/tours/${tour.id}.gpx`

    const gpxFile = await fetch(url)

    if (gpxFile.status !== 200) {
      console.log(gpxFile.status)
      return
    }

    const gpxFileContent = await gpxFile.text()

    return gpxFileContent; // THIS IS IMPORTANT

然后在您的promise.all()中,()执行此操作:

let tourContents =  await Promise.all(
  tours.map(async tour => {
    // ...

如果您要顺序获取(也就是说,一次仅获取一件事),则需要用for loop替换array.prototype.map()

  let tourContents = []

  for (tour of tours) {
    try {
      const url = `https://www.exampleTourApi.com/tours/${tour.id}.gpx`

      const gpxFile = await fetch(url)

      if (gpxFile.status !== 200) {
        console.log(gpxFile.status)
        return
      }

      console.log(`Get content for ${url}`)

      const gpxFileContent = await gpxFile.text()

      console.log(`Content length: ${gpxFileContent.length}`)

      tourContents.push(gpxFileContent)

      console.log('Pushed to array')
    } catch (error) {
      console.log(`Unexpected error ${error}`)
      return
    }
  }

About the output order. You are executing all fetch(url) in parallel, therefore the completion order depends on which request responds first. That's why you should not push the result into an array manually. Instead you should return the result. Promise.all() ensures all results are placed in the same order as the array you pass to it:

tourContents.push(gpxFileContent) // delete this line

Change your code to this:

    const url = `https://www.exampleTourApi.com/tours/${tour.id}.gpx`

    const gpxFile = await fetch(url)

    if (gpxFile.status !== 200) {
      console.log(gpxFile.status)
      return
    }

    const gpxFileContent = await gpxFile.text()

    return gpxFileContent; // THIS IS IMPORTANT

Then in your Promise.all() do this:

let tourContents =  await Promise.all(
  tours.map(async tour => {
    // ...

If on the other hand you want to fetch sequentially (that is, fetch only one thing at a time) you need to replace Array.prototype.map() with a for loop:

  let tourContents = []

  for (tour of tours) {
    try {
      const url = `https://www.exampleTourApi.com/tours/${tour.id}.gpx`

      const gpxFile = await fetch(url)

      if (gpxFile.status !== 200) {
        console.log(gpxFile.status)
        return
      }

      console.log(`Get content for ${url}`)

      const gpxFileContent = await gpxFile.text()

      console.log(`Content length: ${gpxFileContent.length}`)

      tourContents.push(gpxFileContent)

      console.log('Pushed to array')
    } catch (error) {
      console.log(`Unexpected error ${error}`)
      return
    }
  }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文