我对redux saga
有疑问。我已经写了一个包装器,但是该包装器无法与async
动作一起使用。
通常,我的野鹿看起来像这样:
const myAsyncAction = async data => // Do async stuff
function* mySaga(action) {
yield put({
type: SET_LOADING
payload: true
})
try {
const result = yield call(myAsyncAction, action.payload)
yield put({
type: ACTION_SUCCESS
payload: result
})
} catch (error) {
console.log(error)
yield put({
type: SET_ERROR,
payload: error
})
yield put({
type: ACTION_FAIL
})
}
yield put({
type: SET_LOADING
payload: false
})
}
function* watchMySaga() {
yield takeLatest(ACTION, mySaga)
}
export default function* rootSaga() {
yield all([
watchMySaga()
])
}
如您所见,mySaga
遵循一个模式。
首先,它通过将loading
设置为true
来告知应用程序正在执行异步操作。
之后,它将尝试执行异步操作。如果没有错误,则将ACTION_SUCCESS
放入结果。如果发现错误,它将记录该错误并放入ACTION_FAIL
。
完成所有操作后,它将loading
设置为false。
由于几乎所有的sagas都遵循这种模式,因此我认为将其中的一些抽象化是有意义的。我这样做的方法是编写一个包装器。
function* wrapper(saga, args) {
let result
yield put({
type: SET_LOADING
payload: true
})
try {
result = yield call(saga, ...args)
} catch (error) {
console.log(error)
yield put({
type: SET_ERROR,
payload: error
})
}
yield put({
type: SET_LOADING
payload: false
})
return result
}
使用这个包装器,我期望像这样重写mySaga
。
function* mySaga(action) {
const result = yield call(wrapper, myAsyncAction, [ action.payload ])
yield put({
type: result === undefined ? ACTION_FAIL : ACTION_SUCCESS,
payload: result
})
}
如果myAsyncAction
不是async
,这实际上可以工作。常规功能将完美运行。但是,那对我没有太大帮助。
在myAsyncAction
确实是async
的情况下,使用包装器将触发无限循环。
调试时,我注意到了这种情况。触发ACTION
时,代码从观察程序运行到包装程序内部和async函数内部的传奇。它可以在异步操作中完美运行,直到达到await
。当代码到达await
时,出于某种原因再次调度ACTION
,再次开始循环。
我不确定为什么会这样,我真的很好奇为什么会这样。
作为一种可能的解决方案,我正在考虑将ACTION_SUCCESS
和ACTION_FAIL
发送到包装器,并从返回结果更改为将其实际放入包装器中。
但是,我认为那是有限的,也许我不想发表结果,也许我想对它做其他事情。
您知道为什么会发生这种情况吗?有什么可能的解决方案?
谢谢!
TLDR:将yield call
与异步功能一起使用时会陷入无限循环,因为到达await
时,触发动作将再次分派。