使用 async…wait
dva.js 封装了 redux 和 redux-saga,是的数据流处理简单易用,所以用了 umi 之后,所有的数据(状态)都用的是 dva。
尽管如此,写起来有点费劲,假设有一个后端请求 queryUsers
要写好几个地方:
dispatch
发出请求function queryUsers() { this.props.dispatch({ type: 'user/queryUsers', }) }
写
model/effects
*queryUsers(_, { call, put }) { const resp = yield call(queryUsers); yield put({ type: 'saveUsers', data: resp.data, }); },
写
services
export async function queryUsers() { return request('/api/v1/users'); }
最后再写
model/reducers
saveUsers(state, { data }) { return { ...state, users: data }; },
- 实际上还需要将 models.js 与 Component
connect()
起来
(这就是 redux+redux-saga 的方式)写起来比较麻烦,但是习惯了还好,代码结构比较清晰。但是遇到请求之间有依赖关系就比较麻烦了。比如说:
- 首先要通过
id
查到用户明细user-detail
- 然后通过
user-detail
中的username
查询订单列表order_list
使用 dva 我一般是这么做的,
- dispatch
user/queryUserDetail
设置 model state 中的 userDetail - model state
connect
到组件的 props 中 - 在 React 组件中实现
componentDidUpdate
,判断 userDetail 状态是否变更- 如果 userDetail 发生了变更,则 dispatch
user/queryUserOrderList
- 如果 userDetail 发生了变更,则 dispatch
因为全是异步的原因,所以只能通过「状态」同步来判断前一个操作是否已经执行完了。
我们可以通过同步的方式试一下:
request('/api/v1/users'). then(resp => { this.setState({ userDetail: resp.data, }); request(`/api/v1/user/${resp.data.username}/orders`). then(resp => { this.setState({ userOrders: resp.data, }); }) })
这样写是可以的,但是一但同步的调用多了,就会陷入 Javascript 常见的 Callback Hell (因为一直在 resp 后继续调用 request)。
事实上我们需要的是「异步的同步」,每个操作异步调用结束之后,再执行剩下的操作。
ES6 之后引入了 async...await
的调用方式,上面可以改写成:
queryUsers = async () => { const resp = await request('/api/v1/users'); this.setState({ userDetail: resp.data, }) this.queryOrders(resp.data.id); } queryOrders = async (userId) => { const resp = await request(`/api/v1/user/${userId}/orders`); this.setState({ userOrders: resp.data, }) }
看起来就舒服很多了。 await
还可以连续多个并行调用,比如:
const a = await fetchA(); const b = await fetchB(); console.log(a, b);
console.log
会在 fetchA
和 fetchB
执行完毕之后再执行,但 fetchA
和 fetchB
是并行的。有点像 C++ 线程的 join 函数。
回到最初的问题,很多时候使用 redux 的方式写是比较麻烦的。正如 redux 作者写的 You Might Not Need Redux 一样。 我一开始就用的 dva,理所当然的就觉得这种写法是正确的,写了两年了,突然回过头来看,觉得完全没必要,浪费了很多时间…
了解技术不能浅尝辄止,深入浅出才好。
扩展资料: