【问题标题】:Merging various backend requests in the express res.send()在 express res.send() 中合并各种后端请求
【发布时间】:2020-10-22 12:54:35
【问题描述】:

我正在尝试进行多个异步后端调用,以在我的 express API 中生成 JSON 响应。由于 API 的性质,我有 3 个请求以某种方式相互依赖。

请求 1:返回用于发出请求 2 的值数组。每个值将用作其余请求的映射。也就是说,它将是一个唯一标识符,用于映射请求 3 中请求的响应。

请求 2(并行批处理):使用请求 1 中返回的数组中的每个值发出请求。每个请求都返回要在每个请求 3 中使用的值。也就是1对1

请求 3(并行批处理):此请求从请求 2 中获取响应,并发出一对一的后续请求以获取有关该特定映射的更多数据(请求 1 中的 id)

我希望发送给消费者的最终数据如下所示:

{
  id1: details1,
  id2: details2,
  id3: details3,
  ...
}

这是我到目前为止的代码......

app.get("/artists/:artist/albums", (req, res) => {
  console.log("#############")
  const artistName = req.params.artist
  let response = {};
  let s3Promise = s3.listAlbums(artistName)
  let albumDetailsPromises = []

  s3Promise
    .then((data) => {
      data.map((album) => {
        // Each album name here will actually be used as the unique identifier for 
        // the final response
        
        // Build an Array of promises that will first fetch the albumId, then use
        // that album id to fetch the details on the album
        albumDetailsPromises.push(
          discogs.getAlbumId(artistName, album).then(  // Returns a promise
            ({ data }) => {
              let masterId = data.results[0].id
              let recordName = data.results[0].title

              // Storing the album name to carry as a unique id alongside the promise
              return [album, discogs.getAlbumDetails(masterId) // Returns a promise ]
            }
          )
        )
      })
    })
    .then(() => {
      // When all the albumIds have been fetched, there will still exist a promise in the 
      // second index of each element in the albumDetailsPromises array
      Promise.all(albumDetailsPromises)
        .then((namedPromises) => {
          namedPromises.map(
            (album) => {
              let albumName = album[0]  // Unique Id
              let albumDetailPromise = album[1] 

              // Resolving the albumDetailsPromise here, and storing the value on
              // a response object that we intend to send as the express response
              albumDetailPromise
                .then(
                  ({ data }) => {
                    response[albumName] = data
                  })
                .catch(err => response[albumName] = err)
            })
        })
    })
    .catch((err) => console.log(err))
})

到目前为止,一切似乎都按预期工作,我似乎无法弄清楚如何“等待”在所有这些 Promise 结束时更新的响应对象。我在这个例子中省略了res.send(response),因为它不起作用,但这当然是我想要的结果。

感谢任何建议! JavaScript 新手...

【问题讨论】:

    标签: javascript node.js express asynchronous callback


    【解决方案1】:

    我建议使用async/await 重写它,因为它有助于减少嵌套。您还可以将获取专辑详细信息的逻辑提取到单独的函数中,因为这也增加了代码的可读性。像这样的东西(这仍然需要错误处理,但它应该给你一个开始):

    app.get("/artists/:artist/albums", async (req, res) => {
        const artistName = req.params.artist;
        const albumNames = await s3.listAlbums(artistName);
        const result = {};
        
        const albumDetailPromises = albumNames.map(albumName => requestAlbumDetails(discogs, artistName, albumName));
        const resolvedAlbumDetails = await Promise.all(albumDetailPromises);
        
        // map desired response structure
        for(const albumDetail of resolvedAlbumDetails) {
          result[albumDetail.albumName] = albumDetail.albumDetails;
        }
        
        res.json(result);
    });
    
    async function requestAlbumDetails(service, artistName, albumName) {
        const albumInfo = await service.getAlbumId(artistName, albumName);
        const masterId = albumInfo.results[0].id;
        const albumDetails = await service.getAlbumDetails(masterId);
        return { albumName, albumDetails };
    }
    

    要回答您的问题,如何使用您的代码做到这一点: 您需要等待使用另一个 Promise.all 调用来完成所有详细信息,然后只需 send then-handler 中的响应:

    Promise.all(albumDetailsPromises)
      .then((namedPromises) => {
        const detailsPromises = namedPromises.map(
          (album) => {
            let albumName = album[0];
            let albumDetailPromise = album[1];
    
            return albumDetailPromise
              .then(({ data }) => {
                response[albumName] = data;
              })
              .catch(err => response[albumName] = err);
          });
        return Promise.all(detailsPromises)
          .then(() => res.json(response));
      })
    

    【讨论】:

    • 感谢您花时间回答。我尝试使用 async / await 进行重构,它更加清晰。感谢您的建议
    • 很高兴为您提供帮助,如果您接受我的回答,我将不胜感激,谢谢 :)
    【解决方案2】:

    使用 async/await 重构...

    app.get("/artists/:artist/albums", async (req, res) => {
      const artistName = req.params.artist
      let response = {};
    
      let albums = await s3.listAlbums(artistName)
      const promises = albums.map(async (album) => {
        let result = await discogs.getAlbumId(artistName, album)
        try {
          let masterId = result.data.results[0].id
          let tempRes = await discogs.getAlbumDetails(masterId)
          return [album, tempRes.data]
        } catch (error) {
          return [album, { "msg": error.message }]
        }
      })
    
      responses = await Promise.all(promises)
      responses.map(data => { response[data[0]] = data[1] })
    
      res.send(response)
    })
    
    

    【讨论】:

      猜你喜欢
      • 2021-12-03
      • 2022-06-16
      • 2010-09-12
      • 2018-06-10
      • 2021-11-07
      • 1970-01-01
      • 1970-01-01
      • 2023-04-09
      • 1970-01-01
      相关资源
      最近更新 更多