跳到主要內容

錯誤處理

在此區段中,我們將了解如何處理前一個範例的失敗案例。假設我們的 API 函式 Api.fetch 傳回一個 Promise,當遠端擷取因某種原因失敗時,這個 Promise 會被拒絕。

我們想要在 Saga 內部處理這些錯誤,方法是發布 PRODUCTS_REQUEST_FAILED 動作到 Store。

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

為了測試失敗案例,我們將使用產生器的 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 方法。這會導致產生器中斷目前的流程並執行 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 勾子

分岔任務中的錯誤會浮現到父層,直到被捕捉或傳遞到根元程式之前。如果錯誤傳遞到根元程式時,整個元程式樹早已經結束。在此情況下,建議採用onError hook,以回報例外狀況、告知使用者發生問題,並優雅結束應用程式。

為什麼不能將onError hook 當成全域錯誤處理程式來使用?一般而言,並無一勞永逸的解決方案,因為例外是由情境決定的。可以將onError hook 視為處理意外錯誤的最後手段。

如果我不想錯誤浮現要怎麼辦?可以考慮使用安全包裝器。範例可以在這裡找到