'yield all' в Saga не ждет завершения всех эффектов - PullRequest
0 голосов
/ 30 октября 2018

У меня есть Сага (псевдокодиш).

Saga1 вызывает API. Исходя из результата, мне нужно вызвать еще два API. Если все API работают успешно, я вызываю onSuccess, иначе onFailure.

Код, кажется, почти нормально работает, но не совсем. Проблема, с которой я столкнулся с yield all, заключается в том, что он считал saga2 и saga3 завершенными, как только был назван первый результат (см. Комментарий в saga2 / 3). Он не дождался окончания сбора урожая.

Я думаю, что это отчасти связано с моим неправильным пониманием того, что означает «полный эффект». Но помимо этого я хочу уступить всем, чтобы подождать, пока все не будет сделано. Я хочу, чтобы все исключения, сгенерированные fetch в saga2 / 3, были пойманы catch в saga1.

saga1(action) {

    const { onSuccess, onFailure } = action.payload;

    try {
        yield fetch...

        if(response.some_condition) yield all([
            put(saga2()),
            put(saga3())
        ])

        onSuccess();

    }
    catch(e) {
        onFailure();
    }
}

saga2(action) {

    yield put(someaction()) // This yields
    yield fetch...
}

saga3(action) {

    yield put(someaction()) // This yield
    yield fetch...
}

Этот код ниже связан с моим комментарием о том, что catch не работает

action1 () { // action2 is same
    try {
        yield fetch();
        yield put(FINISHED_1);
    }
    catch(e) {
        throw (e);
    }
}

saga1() {
    try {
        yield put(action1());
        yield put(action2());

        yield all([
            take(FINISHED_1),
            take(FINISHED_2),
        ])
        console.log("this doesn't print if exception in either action");
    }
    catch(e) {
        console.log("this doesn't print if exception in either action");
    }
    finally {
        console.log("this prints fine");
    }
}

1 Ответ

0 голосов
/ 31 октября 2018

1) Чтобы дождаться завершения нескольких call эффектов:

yield all([
    call(saga2, arg1, arg2, ...),
    call(saga3, arg1, arg2, ...)
]);

2) Для отправки нескольких действий и ожидания отправки их успешных действий:

yield put(action1());
yield put(action2());

yield all([
    take(ACTION_1_SUCCESS),
    take(ACTION_2_SUCCESS)
]);

Редактирует ответы на комментарии

Если вы вызываете саги напрямую с помощью all (# 1 выше), то вы можете отлавливать ошибки условно

try {
    yield all([
        call(saga2, arg1, arg2, ...),
        call(saga3, arg1, arg2, ...)
    ]);
} catch (e) {
    // ...
}

Но если саги put действия, которые слушают другие саги, эта сага не получает этих исключений. Saga1 не является родителем этих саг или не привязан к ним. Он просто отправляет действия, а некоторые другие задачи в другом месте слушают и отвечают.

Чтобы Saga1 знал об ошибках в этих сагах, саги не должны выдавать ошибки, а вместо этого отправлять действие с полезной нагрузкой:

function* saga2(action) {
    try {
        const result = yield call(...);
        yield put(action2Success(result));
    } catch (e) {
        yield put(action2Failure(e.message));
    }
}

Сага, которая вызывает saga2 (через put(action2())), может обрабатывать успехи и неудачи:

function* saga1(action) {
    yield put(action2());
    yield put(action3());

    const [success, failure] = yield race([
        // if this occurs first, the race will exit, and success will be truthy
        all([
            take(ACTION_2_SUCCESS),
            take(ACTION_3_SUCCESS)
        ]),

        // if either of these occurs first, the race will exit, and failure will be truthy
        take(ACTION_2_FAILURE),
        take(ACTION_3_FAILURE)
    ]);

    if (failure) {
        return;
    }

    // ...
}

Sagas должен обрабатывать исключения и обновлять хранилище с состоянием ошибки, а не генерировать ошибки Бросать ошибки в саги становится грязно при работе с конструкциями параллельной саги. Например, вы не можете напрямую отловить ошибку, выданную задачей fork ed. Кроме того, использование действий для оповещения о результатах саги сохраняет хороший журнал событий в вашем магазине, на который могут реагировать другие саги / редукторы. Когда вы call другие саги, действие, которое должно инициировать эту сагу (например, takeEvery(THE_ACTION, ...)), не отправляется.

...