在 node js 中使用 promise.all 进行外部 api 调用
Posted
技术标签:
【中文标题】在 node js 中使用 promise.all 进行外部 api 调用【英文标题】:Using promise.all to make external api calls in node js 【发布时间】:2020-06-07 02:09:57 【问题描述】:我正在使用 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 not found 错误,有人可以帮我看看我在哪里这里出错了?以下是我尝试过的
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);
);
【问题讨论】:
一小时前与您的其他问题相关:***.com/questions/60358803/… 我想,我没有正确地提出问题,所以我认为最好再发布一个包含更多信息的问题 你不应该有两个关于同一个基本主题的问题。当人们已经处理了您的问题并正在与您合作时,如果您想澄清问题,请使用“编辑”链接修改您的问题,而不仅仅是打开一个新问题。请删除这两个问题之一。 @jfriend00 我已经删除了另一个问题,谢谢 代码中显示的request
变量是request-promise
模块吗?如果是这样,请将其添加到您的代码中,以便显示。当我使用request-promise
模块(顺便说一句已弃用)时,我将其命名为不同的名称,例如rp
,因此阅读我的代码的任何人都不会认为它是常规的request
模块。
【参考方案1】:
此代码应该可以帮助您调试 404:
const rp = require('request-promise');
const requestUrl = "https://stage.tuscanyleather.it/api/v1/";
const categoryRequestUrl = `$requestUrlcategories`;
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 = `$requestUrlproduct-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
将它们混合在一起。
不要将let
或var
用于不会发生变异的事物。这告诉其他程序员和机器不要依赖这个值。使用const
。
请参阅this article 以获得更深入的解释。
使用async
/await
可能更容易做到这一点,至少在调试方面是这样。你有数据结构遍历和异步 monads (Promise),你需要在这里解决,还有一个 404 需要调试。 await
ing 一个 promise 将值扩展到 monad 上下文之外,因此它消除了一层复杂性。
当你有一个 Promises 数组时,你 await Promise.all(arrayP)
会得到一个未包装值的数组。
const rp = require('request-promise');
const requestUrl = "https://stage.tuscanyleather.it/api/v1/";
const categoryRequestUrl = `$requestUrlcategories`;
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 = `$requestUrlproduct-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()
【讨论】:
对于优秀的 forEach,这是一个相当极端的立场。我认为你的 let 和 var 建议在“信号”未来的读者方面是好的。也许我们应该允许 forEach 保留在语言中,作为正在应用程序而不是函数的信号。 查看this article 中的“自定义状态机:循环”部分,看看您的想法。把loop
替换成forEach
,因为是同一个东西,唯一的区别是forEach封装了计数器。
好文章!它应该被转换(映射!)到必修本科CS课程的教学大纲。但我仍然不相信循环。考虑您给出的示例,计算数组中的 Num。 Map 及其变体(如过滤器,当然更漂亮)产生一个中间数组和不需要的第二次迭代。孔子不是也说过“不乱丢”、“看不到不就没有”、“万里长路从一步开始,没有横着走”吗? ?? :-)
在过去几周回答了一堆关于 JS 和节点标签的问题后,我确信这是一把足枪。 :-P 像这个问题一样 - 状态管理、异步和数据转换的交集让您感到疲倦并受到复杂性的影响。在推断要调试之后,您将没有任何脑力。而且您的代码非常脆弱,您不敢触摸它,因为它可能会破坏状态机。以上是关于在 node js 中使用 promise.all 进行外部 api 调用的主要内容,如果未能解决你的问题,请参考以下文章
为啥我的 apolloFetch 调用在从 promise.all 中调用时返回一个空查询?
Node.js / Javascript 多重递归承诺在视图中返回