Redux Saga的async/await模式

59

我在我的代码库中使用async/await。因此,我的API调用是由异步函数定义的。

async function apiFetchFoo {
  return await apiCall(...);
}

我希望能够从我的saga代码中调用此函数。看起来我无法这样做:
// Doesn't work
function* fetchFoo(action) {
  const results = await apiFetchFoo();
  yield put({type: "FOOS_FETCHED_SUCCESSFULLY", foos: results});
}

然而,这确实有效,并符合redux saga文档的要求:
// Does work
function* fetchFoo(action) {
  const results = yield call(apiFetchFoo);
  yield put({type: "FOOS_FETCHED_SUCCESSFULLY", foos: results});
}

这是在使用Redux Saga和async/await时的正确方式吗?在saga代码中使用生成器语法是标准的做法,而在其他地方使用async/await模式吗?


3
在async/await模式中,为什么要使用function *() { ... await }而不是async function () { .. await ...}?我相当确定如果你没有使用async而使用await,会导致一个错误:"await是JavaScript保留关键字"。 - ArchNoob
1
小提示:如果你正在使用TypeScript和typedefs,并且将函数定义为异步函数,例如:async function* fetchFoo(action: requestAction): AsyncGenerator {,则需要将"es2018.asynciterable"包含在tsconfig的compilerOptions.lib数组中(如果尚未包含)。此外,我需要TypeScript 3.7(从3.0开始)。 - Derk Jan Speelman
4个回答

55

是的,这是使用Redux-Saga的标准方法。

您绝不应直接在saga-generator中调用await函数,因为redux-saga用于协调副作用。因此,每当您想要运行副作用时,都应通过redux-saga效果(通常是:callfork)来产生副作用。如果直接执行而没有通过redux-saga效果产生,那么redux-saga将无法协调副作用。

如果您考虑一下,Redux-saga生成器完全可测试,无需模拟任何内容。另外,它有助于保持解耦:如果您的apiFetchFoo返回一个promise,则saga仍将按原样工作。


1
你能否包含任何讨论此问题的文档或Github问题链接?如果有这样一种情况,OP想要await一个没有副作用的Promise怎么办?这似乎是异步生成器的有效用例,但似乎Redux-Saga不允许它们。 - modulitos
3
当然可以! https://redux-saga.js.org/ 我担心您可能不太理解redux-saga的用途。redux-saga生成器旨在协调副作用。如果你想做类似你提到的事情,那么你不应该在redux-saga生成器中完成它...你可以在一个普通生成器中完成它(如果你想要的话),或者在另一个Promise中完成,这并不重要...然后只需在redux-saga生成器中产生一个call效果,调用该Promise/生成器/其他内容即可。 - Josep
2
啊,是的,这很有道理!我觉得我误解了redux-saga生成器的范围,并试图让它做更多的事情。相反,我应该在被yield到saga生成器中的实体中等待“无副作用”的承诺。 - modulitos
仅供参考(可能有助于其他人),这里是react-saga文档中为什么我们不应该在saga中直接调用异步函数的好解释:https://redux-saga.js.org/docs/basics/DeclarativeEffects.html。根据此文档,saga应该将效果yield给saga中间件,saga中间件将执行效果并将结果交还给saga。效果是简单的JS对象,告诉中间件要做什么。将效果yield给saga中间件的整个想法使得saga的单元测试变得简单,无需模拟像API调用之类的异步事物。 - Gaurav Dhiman
可爱的:redux-saga生成器完全可测试,无需模拟任何内容。 - AmerllicA

7
正如Josep所指出的那样,await不能在生成器内使用,必须使用异步函数。此外,请注意这是异步函数本身的限制,而不是redux-saga强加的。
除此之外,我还想提到,redux-saga的作者们有意让开发者无法将sagas表达为async/await函数,这是一个有意的选择
生成器比async/await更强大,它们允许redux-saga的高级功能,如协调并行任务
此外,将sagas表示为生成器有助于我们定义Effects,它们是定义副作用的普通对象。Effects让我们可以非常容易地测试我们的sagas
因此,尽管你的工作代码没问题,但最好不要混用sagas和async函数。只需将apiFetchFoo定义为返回承诺的函数,并在响应请求后使用results来恢复你的saga。
 const apiFetchFoo = () =>
   fetch('foo')
     .then(res => res.json())

0

就像之前的答案所说,Redux saga使用副作用处理其中的异步操作。因此,它的实现方式是使用yield和call,如果调用API等。


-14

await 始终在声明为 async 的函数内工作。 #thumbRule

async function fetchList () {
  let resp = await fetchApi([params]);
}

抱歉,但情况并非总是如此。 - buddhiv

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接