问题
I'm trying to add a redux saga function but I can't get the chaining right
const randomDelay = () => parseInt(Math.random() * 500)
const a = function*() {
yield spawn(b)
yield call(c)
}
const b = function*() {
yield delay(randomDelay())
}
const c = function*() {
yield delay(randomDelay())
}
const d = function*() {}
- I want to call
a
which will spawnb
and callc
. - When
c
is complete I wanta
to become unblocked and complete. - When
b
andc
both complete I want to calld
From what I can tell there isn't a way to do this. all
or fork
will block a
To get around this for now I have c
called first and a combo of b
and d
spawned after but that means b
and c
can't be running at the same time.
回答1:
To do this you need a separate signalling mechanism. I would use a channel
for this.
a
first creates a channela
spawnsdScheduler
passing the channela
passes the channel as an argument tob
b
does aput
to the channel at the enda
does aput
to the channel at the end (whenc
finishes)dScheduler
does twotake
s on the channel and then callsd
The code would look something like the following:
import { delay, channel } from "redux-saga";
import { spawn, call, put, take } from "redux-saga/effects";
const randomDelay = () => parseInt(Math.random() * 500);
const B_OR_C_COMPLETED = "B_OR_C_COMPLETED";
export const a = function*() {
const bcCompletedChannel = channel();
yield spawn(dScheduler, bcCompletedChannel);
yield spawn(b, bcCompletedChannel);
yield call(c);
yield put(bcCompletedChannel, B_OR_C_COMPLETED);
};
const b = function*(bcCompletedChannel) {
yield delay(randomDelay());
yield put(bcCompletedChannel, B_OR_C_COMPLETED);
};
const c = function*() {
yield delay(randomDelay());
};
const dScheduler = function*(bcCompletedChannel) {
yield take(bcCompletedChannel);
yield take(bcCompletedChannel);
yield call(d);
};
const d = function*() {
};
Here's a CodeSandbox with console logs added and the delay lengthened to make it easy to verify the behavior:
The relevant part of the Redux Saga documentation is here. Specifically the section near the bottom called "Using channels to communicate between Sagas".
回答2:
Not as elegant as https://stackoverflow.com/a/54140525/4453205.
Also my answer assumes a
does the job of just calling b
and c
(or two tasks only).
import { delay } from "redux-saga";
import { all, cancel, put, takeEvery, spawn, call } from "redux-saga/effects";
const randomDelay = () => parseInt(Math.random() * 500);
export function* startTasks() {
let completed = yield call(a);
if (completed) {
yield call(d);
}
}
const a = function*() {
let b_finished = false;
const b = function*() {
yield delay(randomDelay());
yield put({ type: "B_DONE" });
b_finished = true;
};
const c = function*() {
yield delay(randomDelay());
yield put({ type: "C_DONE" });
};
const taskB = yield spawn(b);
yield call(c);
yield cancel(taskB);
return b_finished;
};
const d = function*() {
yield delay(randomDelay());
yield put({ type: "D_DONE" });
};
export function* watchStartTasks() {
yield takeEvery("START_TASKS", startTasks);
}
export default function* rootSaga() {
yield all([watchStartTasks()]);
}
来源:https://stackoverflow.com/questions/54137804/redux-saga-how-to-wait-for-spawn-and-call-to-complete-without-blocking-parent-c