我正在开发一个React Native应用程序,我很难让所有异步调用正常工作。
我需要做的一个大致概述是:
deals
(与axios-mock-adapter
模拟)Object
转换为Deal
,其中包括等待以调用Google Maps API来根据文本地址获取坐标。 Deal
s分发到商店。我已将async/await
放在所有可能的位置,并从传奇中的yield call()
开始了第3步,但事情仍然发生着混乱。这是我的代码,后面是步骤的日志记录。抱歉,代码中的数字与上面列表中的数字不完全匹配。
第一个相关的等待失败似乎发生在Deal.collectionFromArray
或其forEach
回调中。尽管await getLocationAsync()
中的AppNavigator.js
也没有先等待。
有人可以帮忙弄清楚为什么这些都不起作用吗?非常感谢。
AppNavigator.js
(摘录)const TabNavigator = createBottomTabNavigator(
{
Map: MapStack,
List: ListStack
},
{ initialRouteName }
);
class TabContainer extends Component<Object> {
static router = TabNavigator.router;
async componentDidMount() {
console.log("getting location");
await this.props.getLocationAsync(); <-- await
console.log("location:", this.props.location);
console.log("getting deals");
await this.props.getDeals(); <-- await
}
render() {
return <TabNavigator navigation={this.props.navigation} />;
}
}
const SwitchNavigator = createSwitchNavigator({
// Auth: AuthNavigator,
Main: connect(
({ location }) => ({ location: location.currentRegion }),
{ getLocationAsync, getDeals }
)(TabContainer)
});
const AppNavigator = createAppContainer(SwitchNavigator);
type AppProps = {
navigation: Object,
getLocationAsync: () => void,
loadingMessage: string
};
class AppContainer extends Component<AppProps> {
static router = TabNavigator.router;
componentDidMount() {
// this.props.getLocationAsync();
}
render() {
return (
<View style={{ flex: 1 }}>
<LoadingIndicator message={this.props.loadingMessage} />
<AppNavigator navigation={this.props.navigation} />
</View>
);
}
}
export default connect(({ location, deals, auth }) => ({
loadingMessage:
auth.loadingMessage || location.loadingMessage || deals.loadingMessage
}))(AppContainer);
dealActions.js
export async function getDealsApi(): Promise<Object[] | Error> {
try {
const res = await axios.get(ApiUrls.getProductsAuthorized); <-- await
console.log("2. API got", res.data.length, "deals (from getDealsApi)");
const deals = await Deal.collectionFromArray(res.data); <-- await
console.log("3. converted", Object.keys(deals).length, "deals with Deal.fromApi (from getDealsApi)" );
return deals;
} catch (error) {
console.warn("get products failed. is axios mock adapter running?");
return error;
}
}
export function* getDealsSaga(): Saga<void> {
try {
console.log("1. calling API (from getDealsSaga)");
const deals: DealCollection = yield call(getDealsApi); <-- yield call()
console.log("4. dispatching", Object.keys(deals).length, "deals (from getDealsSaga)" );
yield put({ type: "GET_DEALS_SUCCESS", deals });
} catch (error) {
console.warn("getDealsSaga", "error:", error);
yield put({ type: "GET_DEALS_FAILURE", error });
}
}
Deal.js
static async collectionFromArray(array: Object[]) {
const deals: DealCollection = {};
await array.forEach(async p => { . <--await
deals[p.id] = await Deal.fromApi(p); <--await
});
console.log("converted:", Object.keys(deals).length);
return deals;
}
static async fromApi(obj: Object): Promise<Deal> {
const deal = new Deal();
deal.id = obj.id;
deal.name = obj.name;
// ...set other properties
if (deal.address) {
const location = await Deal.getLocationForAddress(deal.address); <-- await
deal.location = location;
}
// console.log(deal.location);
return deal;
}
static async getLocationForAddress(address: string): Promise<?Location> {
try {
const search = await fetch(ApiUrls.mapsSearch(address)); <-- await
const { predictions, ...searchData } = await search.json(); <-- await
if (searchData.error_message) throw Error(searchData.error_message);
if (!predictions.length) throw Error("No places found for " + address);
const details = await fetch(ApiUrls.mapsDetails(predictions[0].place_id)); <-- await
const { result, ...detailsData } = await details.json(); <-- await
if (detailsData.error_message) throw Error(searchData.error_message);
const location = result.geometry.location;
return {
latitude: location.lat,
longitude: location.lng
};
} catch (error) {
console.log("Error getting location for address in deal:", error);
}
}
省略了步骤5的代码,这很不言自明。
日志,带注释:
getting location
location: null // even this first call isn't waiting!
getting deals
1. calling API (from getDealsSaga)
2. API got 10 deals (from getDealsApi()) // will this wait when we're calling the actual web API?
converted: 0 // not waiting
3. converted 0 deals with Deal.fromApi (from getDealsApi) // previous log didn't wait so of course this is empty
4. dispatching 0 deals (from getDealsSaga) // ditto. perhaps yield call() is working but all the non-waiting above makes that irrelevant
// after all this is done, the Google Maps calls finally return, nothing is dispatched or rerendered.
lat: 40.78886889999999, long: -73.9745318
lat: 40.78886889999999, long: -73.9745318
lat: 40.78886889999999, long: -73.9745318
// ... and the rest of the coordinates, finally returned
答案 0 :(得分:0)
我的问题似乎是在await
内forEach
。
如this answer中所述,await
内的forEach
无法正常工作。使用for...in
,虽然有点冗长,但可以:
// in Deal.js
async setLocation() {
this.location = await Deal.getLocationForAddress(this.address);
}
// inside of getDealsApi()
for (const id in deals) {
const deal = deals[id];
if (!deal.location && deal.address) await deal.setLocation();
}