JS中如何按顺序调用POST路由函数串行拉取API数据
问题根因
执行顺序错乱的核心原因非常明确:async 声明的函数返回值是 Promise,你在路由回调里调用 getGeoNamesData() 时没有加 await 等待异步请求 resolve,JS 事件循环会直接跳过 pending 状态的 Promise 继续往下执行同步代码,才会出现日志顺序完全不符合预期、经纬度变量还没赋值就被后续接口调用的问题。
另外原有代码还存在两个隐藏风险:
longitude/latitude/userInput用全局变量存储,并发请求时会出现变量覆盖的脏数据问题- 所有异步接口请求没有统一错误兜底,流程结束后也没有给前端返回响应,很容易导致请求挂起、进程异常崩溃
修改方案
核心调整逻辑
- 将路由回调改成
async函数,所有依赖前置异步结果的调用前都加await等待,保证执行顺序符合预期 - 移除全局传参的写法,各个异步函数通过入参拿需要的参数,通过 return 返回执行结果,避免全局变量污染
- 加统一错误捕获,所有流程执行完成后主动给前端返回响应
修改后的完整可运行代码如下:
// 路由回调加async关键字,才能在内部使用await app.post('/postUserInput', async function (req, res) { try { const currentWeatherUI = req.body.buildCurrentBoolean; const userInput = { userCityInput: req.body.userCityInput, userStateInput: req.body.userStateInput, userDateInput: req.body.userDateInput, }; console.log("first"); // 等待GeoNames接口返回结果,直接拿到经纬度,不依赖全局变量存储 const { longitude, latitude } = await getGeoNamesData(userInput); console.log("second"); console.log("third"); if (currentWeatherUI) { await getCurrentWeatherbitData(longitude, latitude, userInput); console.log("fourth"); console.log("fifth"); } else { await getFutureWeatherbitData(longitude, latitude, userInput); }; console.log("sixth"); await getPixabayData(userInput); console.log("seventh"); console.log("eigth"); // 所有流程执行完成后,给前端返回组装好的结果 res.status(200).send({ code: 0, data: projectDataArray }) } catch(err) { // 统一捕获所有环节的异常,给前端返回错误提示 console.log("接口执行异常", err); res.status(500).send({ code: -1, msg: "服务执行异常" }) } }); // 接收userInput入参,请求完成后直接返回经纬度 const getGeoNamesData = async (userInput) => { const response = await axios.get('http://api.geonames.org/searchJSON?q='+userInput.userCityInput+'+'+userInput.userStateInput+'&maxRows=1&username='+geonamesUser); return { longitude: response.data.geonames[0].lng, latitude: response.data.geonames[0].lat } }; // 接收经纬度、用户输入入参,完成后把结果推入数据数组 const getCurrentWeatherbitData = async (longitude, latitude, userInput) => { const weatherbitURL = "http://api.weatherbit.io/v2.0/current?&lat="+latitude+"&lon="+longitude+"&key="+weatherbitUser+"&units=I&lang=en"; const response = await axios.get(weatherbitURL); const newDayObject = { city: response.data.data[0].city_name, state: response.data.data[0].state_code, date: userInput.userDateInput, temp: response.data.data[0].temp, wind: response.data.data[0].wind_spd, windDirection: response.data.data[0].wind_cdir, rain: response.data.data[0].precip, snow: response.data.data[0].snow }; projectDataArray.push(newDayObject); }; // 未来天气查询函数和当前天气逻辑保持一致,补全入参接收、加await等待即可 const getFutureWeatherbitData = async (longitude, latitude, userInput) => { // 原有未来天气请求逻辑保留即可 }; // 接收用户输入入参,完成后把图片结果推入数据数组 const getPixabayData = async (userInput) => { const pixabayURL = "https://pixabay.com/api/?key="+pixabayUser+"&q="+userInput.userCityInput+"+"+userInput.userStateInput+"&image_type=photo&pretty=true&per_page=3"; const response = await axios.get(pixabayURL); let cityImageObj; if (response.data.hits.length > 0) { cityImageObj = {cityImageURL: response.data.hits[0].webformatURL}; } else { cityImageObj = {cityImageURL: "https://www.pennlive.com/resizer/vNu0aYjk3xlFTUb16FSrSji_DIA=/1280x0/smart/advancelocal-adapter-image-uploads.s3.amazonaws.com/image.pennlive.com/home/penn-media/width2048/img/life/photo/wintermeme11.jpg"}; } projectDataArray.push(cityImageObj); };
修改后效果
调整后控制台日志会严格按照 first -> second -> third -> fourth -> fifth -> sixth -> seventh -> eigth 的顺序打印,经纬度会在天气、图片接口调用前完成赋值,不会再出现参数为空的问题。如果需要优化接口性能,在拿到经纬度之后,天气查询和图片查询没有依赖关系,也可以用 Promise.all 并行执行两个请求,减少整体接口响应时间。
内容的提问来源于stack exchange,提问作者GT Thompson
相关产品推荐
相关产品推荐

