跳到主要内容

错误处理

在这一部分,我们将看到如何处理上一个例子中的失败情况。假设我们的API函数 Api.fetch 返回一个Promise,当远程获取因某种原因失败时,该Promise会被拒绝。

我们希望在我们的 Saga 中通过向 Store 分发一个 PRODUCTS_REQUEST_FAILED 动作来处理这些错误。

我们可以使用熟悉的 try/catch 语法在 Saga 中捕获错误。

import Api from './path/to/api'
import { call, put } from 'redux-saga/effects'

// ...

function* fetchProducts() {
try {
const products = yield call(Api.fetch, '/products')
yield put({ type: 'PRODUCTS_RECEIVED', products })
}
catch(error) {
yield put({ type: 'PRODUCTS_REQUEST_FAILED', error })
}
}

为了测试失败情况,我们将使用 Generator 的 throw 方法

import { call, put } from 'redux-saga/effects'
import Api from '...'

const iterator = fetchProducts()

// 期望一个调用指令
assert.deepEqual(
iterator.next().value,
call(Api.fetch, '/products'),
"fetchProducts 应该产生一个 Effect call(Api.fetch, './products')"
)

// 创建一个假错误
const error = {}

// 期望一个分发指令
assert.deepEqual(
iterator.throw(error).value,
put({ type: 'PRODUCTS_REQUEST_FAILED', error }),
"fetchProducts 应该产生一个 Effect put({ type: 'PRODUCTS_REQUEST_FAILED', error })"
)

在这种情况下,我们传递给 throw 方法一个假错误。这将导致 Generator 打断当前流程并执行 catch 块。

当然,你并不一定要在 try/catch 块中处理你的 API 错误。你也可以让你的 API 服务返回一个带有错误标志的正常值。例如,你可以捕获 Promise 拒绝并将它们映射到一个带有错误字段的对象。

import Api from './path/to/api'
import { call, put } from 'redux-saga/effects'

function fetchProductsApi() {
return Api.fetch('/products')
.then(response => ({ response }))
.catch(error => ({ error }))
}

function* fetchProducts() {
const { response, error } = yield call(fetchProductsApi)
if (response)
yield put({ type: 'PRODUCTS_RECEIVED', products: response })
else
yield put({ type: 'PRODUCTS_REQUEST_FAILED', error })
}

onError 钩子

在分叉任务中的错误bubble up to their parents 直到它被捕获或达到根 saga。 如果一个错误传播到根 saga,整个 saga 树已经终止。在这种情况下,首选的方法是使用 onError 钩子 来报告异常,通知用户问题并优雅地终止你的应用。

为什么我不能使用 onError 钩子作为全局错误处理器? 通常,没有一种适合所有情况的解决方案,因为异常是依赖于上下文的。将 onError 钩子视为最后的手段,它可以帮助你处理意外的错误。

如果我不希望错误冒泡怎么办? 考虑使用安全包装器。你可以在这里找到示例。