Redux Saga: How to wait for spawn and call to complete without blocking parent call

耗尽温柔 提交于 2020-01-04 14:17:37

问题


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 spawn b and call c.
  • When c is complete I want a to become unblocked and complete.
  • When b and c both complete I want to call d

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 channel
  • a spawns dScheduler passing the channel
  • a passes the channel as an argument to b
  • b does a put to the channel at the end
  • a does a put to the channel at the end (when c finishes)
  • dScheduler does two takes on the channel and then calls d

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!