How to run synchronous code after redux saga channel closes

Viewed 454

I am trying to execute a code after the saga is cancelled/closed, or after the channel is closed.

This is a simple example:

  1. Create a channel that holds some random numbers
  2. Create some forks to do some expensive calculation on each number.
  3. Add some random numbers to the channel
  4. After the channel is closed, i want to execute some code.

But what im missing is how to execute code after all of this is finished. (in this example, a simple console.log)

Code is here:

const {
    take, fork, put, call, delay, all, join,
} = require('redux-saga/effects');
const { channel, runSaga } = require('redux-saga');

function* doSomeExpensiveThing(citiesChannel) {
    while (true) {
        const payload = yield take(citiesChannel);

        // do some expensive operation with each number
        // simulated here with a random 0-1000ms delay
        yield delay(Math.floor(Math.random() * Math.floor(10)) * 100);
        console.log('Finished number: ', payload);
    }
}

function* bootstrapSaga(numberOfWorkers) {
    console.log('Bootrstrapping saga.');

    // create a channel to queue all incoming numbers
    const numbersQueue = yield call(channel);
    const workers = [];

    // create N worker 'threads' to receive items put into the queue
    for (let i = 0; i < numberOfWorkers; i += 1) {
        workers.push(yield fork(doSomeExpensiveThing, numbersQueue));
    }

    const allNumbers = Array.from(Array(10).keys());

    // add all numbers to the queue
    while (allNumbers.length > 0) {
        yield put(numbersQueue, allNumbers.pop());
    }

    // yield join(workers);
    // yield all(workers.map(w => w.toPromise()));
    // console.log('NEVER HAPPENS');
}

function* mainSaga(numberOfWorkers = 3) {
    console.log('Starting...');

    yield call(bootstrapSaga, numberOfWorkers);
    console.log('ALL DONE.'); // Y U NO WORK?? ¯\_(ツ)_/¯ 
}

runSaga({}, mainSaga, 5);

If it makes it easier, there is a runnable version here: https://codesandbox.io/s/o40kl Be sure to open the console on codesandbox

For this example, i tried a few things, like using yield all(workers) or yield join(workers). These were just attempts to make the console.log('ALL DONE') work.

Question: What do I have to modify on my script in order for it to do so?

1 Answers

Update: In order to have this to work, we will need to close the channel after puting the work to be done by the workers:

numbersQueue.close();

Then we can use all effect to wait for all the tasks to resolve:

yield all(workers.map(w => w.toPromise()));

Creates an Effect description that instructs the middleware to run multiple Effects in parallel and wait for all of them to complete. It's quite the corresponding API to standard Promise#all.

Update: I have updated this codesandbox and added the necessary modifications so you can see it works as expected.

enter image description here

Related