javascript - 使用promise.all在node js中进行外部api调用

标签 javascript node.js api asynchronous promise

我正在使用 request-promise npm 包在 nodeJS 中进行多个 api 调用。为了让我获得所需的数据,我必须循环遍历所有类别,然后在每个类别中循环遍历产品数组以获取产品信息。类别响应示例如下

{
        "success": true,
        "response": [
          {
            "category_id": 9,
            "name": "Leather Wheeled luggage",
            "products": [
              "TL141911",
              "TL141888"
            ],
            "parent_category_id": 34
          },
          {
            "category_id": 10,
            "name": "Leather Luggage Weekender Duffles bags",
            "products": [
              "TL141794",
              "TL141658"

            ],
            "parent_category_id": 34
          }
        }

因为我必须循环并进行 api 调用,所以我尝试使用 Promise.all,但它没有产生正确的结果,它向我抛出 404 未找到错误,有人可以帮助我吗?这里出问题了吗?以下是我尝试过的

const rp = require('request-promise');

const requestUrl = "https://stage.tuscanyleather.it/api/v1/";
const categoryRequestUrl = requestUrl + 'categories';
let tuscanApiOptions = {
    uri: categoryRequestUrl,
    headers: {
        'Authorization': 'Bearer asd343'
    },
    method: 'Get',
    json: true 
};

rp(tuscanApiOptions)
    .then((categResponse) => {
        //res.end(categResponse);  
        let ps = [];
        categResponse.response.forEach((category) => {
            if (category.products !== undefined) {
                category.products.forEach((product) =>{
                    let productApiOptions = {
                        uri: requestUrl + `product-info?code=${product}`,
                        headers: {
                            'Authorization': 'Bearer asd343'
                        },
                        method: 'Get',
                        json: true 
                    };
                    ps.push(rp(productApiOptions));
                });
                Promise.all(ps)
                    .then((values) => {
                        console.log(values); //This is where things are going wrong
                  })
                    .catch((err) =>{
                        console.log(JSON.stringify(err,null,2));
                    })
            }

        })

    })
    .catch((error) => {
        console.log(error);
        //res.status(error.statusCode).send(error.error.error_description);
    });

最佳答案

此代码应该可以帮助您调试 404:

const rp = require('request-promise');

const requestUrl = "https://stage.tuscanyleather.it/api/v1/";
const categoryRequestUrl = `${requestUrl}categories`;
const tuscanApiOptions = {
  uri: categoryRequestUrl,
  headers: {
    'Authorization': 'Bearer asd343'
  },
  method: 'Get',
  json: true 
};

// Promise of flat array of products (loses category data)
const productsP = rp(tuscanApiOptions))
  .then(({response}) => response
    .map(category => category.products) // array of arrays
      .flat() // flatten to array of products
      .filter(p => !!p) // remove empty 
  )

const prodUrl = `${requestUrl}product-info?code=`

const productDataP = productsP.then(products => 
  products.map(product => 
    rp({
      ...tuscanApiOptions,
      uri: `${prodUrl}${product}` 
    }).catch(e => `Error fetching ${prodUrl}${product} ${e.message}`)
  )
)

// Wait for all requests to resolve with data or error. 
// resultsP is an array of Promise<productdata|error>
const resultsP = Promise.all(productsDataP)

resultsP.then(console.log)             

一些建议,要么接受,要么放弃:

Array.forEach从你的编程词汇中剔除。它会给你带来比它解决的问题更多的问题,因为它是副作用的(本质上是一个循环)。使用 Array.map 返回一个新数组,然后对其进行操作。 Array.map 将状态机与数据转换分开,forEach 将它们混合在一起。

不要将 letvar 用于不会发生变化的事物。这告诉其他程序员和机器不要依赖该值。使用const

参见this article以获得更深入的解释。

使用async/await可能更容易做到这一点,至少调试起来是这样。您需要处理数据结构遍历和异步 monad(Promise),以及需要调试的 404。 await promise 将值扩展到 monad 上下文之外,因此消除了一层复杂性。

当您有 Promise 数组时,您等待 Promise.all(arrayP) 来获取未包装值的数组。

const rp = require('request-promise');

const requestUrl = "https://stage.tuscanyleather.it/api/v1/";
const categoryRequestUrl = `${requestUrl}categories`;
const tuscanApiOptions = {
  uri: categoryRequestUrl,
  headers: {
    'Authorization': 'Bearer asd343'
  },
  method: 'Get',
  json: true 
};

async function main() {
  const categories = await rp(tuscanApiOptions)
  const products = categories.response
    .map(category => category.products) 
    .flat() 
    .filter(p => !!p) // remove undefined

  console.log(products) // should be flat array of products

  const prodUrl = `${requestUrl}product-info?code=`

  const requestsP = products
    .map(product => 
      rp({
        ...tuscanApiOptions,
        uri: `${prodUrl}${product}` 
      }).catch(e => `Error fetching ${prodUrl}${product} ${e.message}`)
  )

  const results = await Promise.all(requestsP)

  console.log(results)
}

main()   

关于javascript - 使用promise.all在node js中进行外部api调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60359096/

相关文章:

javascript - 当 items 是数组时,React items.map 不是函数?

android - Google Map Api 中的 PlaceAutoComplete

javascript - 在 Highcharts 中隐藏特定图表宽度的数据标签

javascript - 在javascript中获取相对于另一个url的url

node.js - 使用 map 在 Sequelize 中创建一个新的普通对象

mysql - 在 sequelize 中聚合来自嵌套模型的数据

node.js - `RSV2 and RSV3 must be clear` 中的错误 `ws`

javascript - Bootstrap 菜单更改事件类更改不起作用

javascript - 使用nodejs查询MongoDb

android - Android 上的传入邮件通知