React Native Axios/Promise嵌套调用API的异步问题解决
问题场景
个人项目接入Yelp Fusion API时用到两类接口:
- 一类可根据位置参数返回餐厅ID数组
- 一类可根据餐厅ID返回对应餐厅的详情信息
核心需求是先获取餐厅ID数组,再遍历ID拉取对应详情替换原数组中的条目,最终得到完整的餐厅详情数组,项目中使用Axios发起网络请求,原实现代码如下:
//Run the Business Search API to get 50 results, based on location and filters function getRestaurantsFromYelp() { var axios = require('axios'); var config = { method: 'get', url: `https://api.yelp.com/v3/businesses/search?limit=50&latitude=${lat}&longitude=${long}`, headers: { 'Authorization': 'Bearer [[authorization token]]' } }; axios(config) .then(async function (response) { var restaurants = response.data.businesses //array of 50 restaurants try { for (let i = 0; i < 3; i++) { let id = restaurants[i].id; let details = getRestaurantDetails(id); restaurants[i] = details; } } catch (error) { console.log(error) } setRestaurantData(((restaurants))); }) .catch(function (error) { console.log(error); }); } async function getRestaurantDetails(id) { var axios = require('axios'); var config = { method: 'get', url: `https://api.yelp.com/v3/businesses/${id}`, headers: { 'Authorization': 'Bearer [[authorization token]]' } }; await axios(config) .then(function (response) { return response.data; }) .catch(function (error) { console.log(error); }); return; }
当前问题:details变量取值为undefined,餐厅详情接口返回时机晚于变量赋值时机。
备注:最终需要拉取全部50家商户的详情信息,当前调试阶段暂时将for循环设置为仅遍历前3个元素。
问题根因
代码里有两处核心的async/await逻辑错误:
- 调用
getRestaurantDetails时没有加await关键字。该函数是async函数,默认返回Promise对象,不加await直接赋值的话,拿到的是未决议的Promise,不会等接口返回结果。 getRestaurantDetails函数末尾写了空的return;,相当于主动返回undefined,就算axios请求成功拿到了response.data,也被这个空返回给覆盖了,函数根本不会把接口数据抛出去。
另外还有个冗余问题:把require('axios')写在函数内部,每次调用函数都会重复执行模块加载逻辑,没有必要。
修复后可运行代码
// 顶部统一引入axios,不要在函数内重复require const axios = require('axios'); // 统一复用请求头,减少重复代码 const YELP_REQUEST_HEADERS = { 'Authorization': 'Bearer [[authorization token]]' }; async function getRestaurantDetails(id) { try { const config = { method: 'get', url: `https://api.yelp.com/v3/businesses/${id}`, headers: YELP_REQUEST_HEADERS }; const response = await axios(config); // 直接返回接口拿到的详情数据 return response.data; } catch (error) { console.log(`获取ID为${id}的餐厅详情失败:`, error); // 请求失败返回null,做兜底避免整条数据为空 return null; } } async function getRestaurantsFromYelp(lat, long) { try { const searchConfig = { method: 'get', url: `https://api.yelp.com/v3/businesses/search?limit=50&latitude=${lat}&longitude=${long}`, headers: YELP_REQUEST_HEADERS }; const searchResponse = await axios(searchConfig); const restaurants = searchResponse.data.businesses; // 调试阶段先遍历前3个,逻辑跑通后把循环上限改成restaurants.length即可拉取全部50家 for (let i = 0; i < 3; i++) { const restaurantId = restaurants[i].id; // 必须加await,等详情接口返回后再赋值 const detailData = await getRestaurantDetails(restaurantId); if (detailData) { restaurants[i] = detailData; } } setRestaurantData(restaurants); } catch (error) { console.log('获取Yelp餐厅列表失败:', error); } }
优化建议
- 串行for循环请求50个接口速度较慢,调试通逻辑后可以换成
Promise.all并发请求,速度会提升很多。如果触发Yelp API的限流规则,可以分批并发,比如一次发10个请求,一批完成后再发下一批。并发写法参考:
// 替换原来的for循环逻辑即可 const detailPromiseList = restaurants.map(item => getRestaurantDetails(item.id)); const fullRestaurantList = await Promise.all(detailPromiseList); // 过滤掉请求失败返回null的项,也可以根据需求保留原简版数据 setRestaurantData(fullRestaurantList.filter(Boolean));
- 不要把鉴权token硬编码在代码里,建议放到环境变量中存储,避免泄露。
内容的提问来源于stack exchange,提问作者Priya Agarwal
相关产品推荐
相关产品推荐

