跳至主要内容

错误处理

在本节中,我们将了解如何处理上一示例中的失败情况。假设我们的 API 函数 Api.fetch 返回一个 Promise,当远程获取由于某种原因失败时,该 Promise 会被拒绝。

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

我们可以使用熟悉的 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()

// expects a call instruction
assert.deepEqual(
iterator.next().value,
call(Api.fetch, '/products'),
"fetchProducts should yield an Effect call(Api.fetch, './products')"
)

// create a fake error
const error = {}

// expects a dispatch instruction
assert.deepEqual(
iterator.throw(error).value,
put({ type: 'PRODUCTS_REQUEST_FAILED', error }),
"fetchProducts should yield an 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 钩子

分叉任务中的错误 会冒泡到它们的父级,直到被捕获或到达根 Saga。如果错误传播到根 Saga,整个 Saga 树将被终止。在这种情况下,首选方法是使用 onError 钩子 报告异常,通知用户问题并优雅地终止您的应用程序。

为什么我不能使用 onError 钩子作为全局错误处理程序?通常,没有一刀切的解决方案,因为异常是依赖于上下文的。将 onError 钩子视为最后的手段,可以帮助您处理意外错误。

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