在 Node 中实现并发 API 调用
问题
在构建后端 API 时,我们通常需要从第三方 API 获取数据,对其进行清理、格式化和合并,然后将其转发到前端。
例如,我们可以利用 NASA 的公开 API 获取任意日期的每日天文照片 (
APOD )。但是,它不支持获取多个日期范围内的照片。现在假设我们需要构建一个后端 API,能够返回指定天数的 APOD 列表,我们应该怎么做?
我首先想到的是生成一个包含一系列日期的数组。然后我可以使用 forEach 方法或 for 循环遍历该数组,逐个调用 API 获取数据,将其放入结果数组,最后将结果返回给前端。然而,即使这种方法可行,也与目标不符,因为目标需要并发调用。使用 forEach 或 for 循环仍然只能按顺序执行,而不能同时执行。这种方法速度慢,效率低。
经过一番研究,我发现了一个名为async 的库,它完美地满足了我的需求。async 库提供了各种用于处理异步 JavaScript 的函数。
在这个例子中,我们将使用并行方法,它主要用于流程控制:
parallel(tasks, callback)
它允许我们并行运行多个任务,而无需等待前一个函数完成。结果以数组的形式传递给回调函数。
我们开始吧。
解决方案
首先,我们需要创建一个辅助函数,它接受天数作为参数,并返回一个日期数组。NASA 的 API 只接受 YYYY-MM-DD 格式的日期,例如,如果今天的日期是 2020-12-23,天数为 6,则返回的数组将是:
[
'2020-12-18',
'2020-12-19',
'2020-12-20',
'2020-12-21',
'2020-12-22',
'2020-12-23'
]
以下是该函数的代码:
function generatedates(numberOfDays) {
const result = []
const today = new Date()
for (let i = 0; i < numberOfDays; i++) {
let date = new Date(today)
date.setDate(today.getDate() - i)
let dd = date.getDate()
let mm = date.getMonth() + 1
let yyyy = date.getFullYear()
if (dd < 10) {
dd = "0" + dd
}
if (mm < 10) {
mm = "0" + mm
}
date = yyyy + "-" + mm + "-" + dd
result.unshift(date)
}
return result
}
然后我们需要向节点服务器添加一个端点。
/api/photos
并行函数以函数数组作为第一个参数,因此我们可以使用 map 方法遍历日期数组并返回函数数组。数组中的每个函数都会向 NASA API 发起 Axios 调用,并获取对应日期的图片。
并行函数的第二个参数是回调函数。在本例中,由于 API 调用返回的是 Promise,回调函数会返回两个值。第一个值是可能的错误,第二个值是结果数组。
如果不需要对数据进行进一步处理,我们可以直接将其传递给前端。我们还可以使用 forEach 方法来清理数据,只提取我们需要的信息。
以下是该端点的逻辑:
const URL = "https://api.nasa.gov/planetary/apod"
server.get("/api/photos", (req, res) => {
const days = req.query.days
const dates = generateDates(days)
const functionArray = dates.map((date) => {
return async function () {
const data = await axios.get(`${URL}?api_key=${api_key}&date=${date}`)
return data.data
}
})
async.parallel(functionArray, (err, result) => {
res.status(200).json({ items: result.length, photos: result })
})
})
现在用户可以通过 API 请求获取任意数量的照片,例如:
//fetch photos of the past week
api/photos?days=7
//fetch photos of the past month
api/photos?days=30
结果将显示为:
{
"items": 6,
"photos": [...]
}
代码
请查看此示例仓库的 GitHub 代码库
