redux-saga:令牌刷新后重试捕获的操作

时间:2018-07-18 11:34:38

标签: javascript asynchronous redux-saga saga

我在redux-saga频道中进行了一组操作。它们中的每个(或同时)可能由于令牌过期而失败。 如何暂停以后的操作以防止下次失败,在刷新令牌和取消暂停通道后重试已捕获的操作。

function* watchRequests() {
    // a set of simultaneous actions which utilize access token from store
    const requestChannel = yield actionChannel('*_REQUEST')
    while (true) {
        try {
            const { payload } = yield take(requestChannel)

            yield fork(handleRequest, payload)
        } catch (error) {
            if (error === 'EXPIRED_ACCESS_TOKEN') {
                const newToken = yield call(refreshToken)
                yield call(setAccessToken, newToken)
            } else {
                throw error
            }
        } finally {
            // after success token refresh
            // 1. retry catched action
            // 2. unsuspend rest actions from channel
        }
    }
}

1 个答案:

答案 0 :(得分:2)

在这里处理相同的概念,但是使用不同的配方。

下面是代码的关键部分,但请注意,它仍在进行中。

关键是派生了“ api.refreshSession”,并且在此任务仍在运行时,每个REFRESH_SESSION操作都将加入已在运行的任务,而不是启动新任务。

function* refreshSessionListener() {
    let task;
    while (true) {
        const action = yield take(REFRESH_SESSION);
        if (task === undefined || !task.isRunning()) {
            task = yield fork(api.refreshSession);
        }
        // non-blocking join
        yield fork(joinRefreshSessionTask, task, action);
    }
}

function* joinRefreshSessionTask(task, action) {
    const response = yield join(task);
    if (response.status === 200) {
        yield fork(action.onSuccess);
    } else {
        yield fork(action.onError, response);
    }
}

export function* apiSaga(...args) {
    const [fn, { type, payload, meta = {} }] = args;
    try {
        const data = yield call(fn, payload);
        yield* okSaga(type, e);
    } catch (e) {
        if (e.tokenExpired) {
            yield put({
                type: REFRESH_SESSION,
                onSuccess: apiSaga.bind(null, ...args),
                onError: errorSaga.bind(null, type)
            });
            return;
        } else {
            yield* errorSaga(type, e);
        }
    }
}

runSaga(refreshSessionListener)

我在应用程序的其他部分使用上述内容:

import {apiSaga} from ...

function* listenToActions () {
    yield takeEvery(POST_SOMETHING, apiSaga.bind(null, api.postSomething));
    yield takeLatest(GET_SOMETHING, apiSaga.bind(null, api.getSomething));
}

runSaga(listenToActions);