What's the difference between fork and spawn in redux-saga?

末鹿安然 提交于 2019-11-30 08:10:39
Alex

One way to look at it is to see your saga's as a Graph. 'fork' creates a child from the calling process. While 'spawn' creates a new child at the root of the graph.

So when you 'fork' another process, the parent process will wait until the 'forked' process is finished. Also every exception will bubble up from the child to the parent and can be caught in the parent.

A 'spawned' process though will not block the parent, so the next 'yield' statement is called immediately. Also the parent process will not be able to catch any exceptions that happen in the 'spawned' process.

I hope this was helpful.

In the same docs it says:

When the parent terminates the execution of its own body of instructions, it will wait for all forked tasks to terminate before returning.

Let's say we have this setup where in the middle of execution flow we call fetchAll() that might call either fork or spawn:

const { delay, runSaga } = require("redux-saga");
const fetch = require("node-fetch");
const { fork, spawn, call, put} = require("redux-saga/effects");


function* fetchResource(resource) {
    console.log(`Fetch ${resource} start`);
    const response = yield call(fetch, "https://jsonplaceholder.typicode.com" + resource);
    const text = yield call(response.text.bind(response));
    console.log(`Fetch ${resource} end`);
}

function* fetchAll() {
    console.log("Fork or Spawn start");
    // this is pseudo code, I mean here that you can use either
    // fork or spawn, check results below
    const task1 = yield fork||spawn(fetchResource, "/posts/1"); 
    const task2 = yield fork||spawn(fetchResource, "/posts/2");
    console.log("Fork or Spawn end");
}

function* main() {
    console.log("Main start");
    yield call(fetchAll);
    console.log("Main end");
}

runSaga({}, main);

// RESULTS WITH FORK():   |  RESULTS WITH SPAWN():
//                        |
// Main start             |  Main start
// Fork start             |  Spawn start
// Fetch /posts/1 start   |  Fetch /posts/1 start
// Fetch /posts/2 start   |  Fetch /posts/2 start
// Fork end               |  Spawn end
// Fetch /posts/2 end     |  Main end <-- 
// Fetch /posts/1 end     |  Fetch /posts/2 end
// Main end <--           |  Fetch /posts/1 end

What we see is, within call context the fork is non-blocking, but call is not going to finish until all its children processes are finalised, since call itself is a blocking effect.

You wouldn't see the same if you call a fork within another fork, since fork itself is non-blocking, and inner forked processes would leak out of outer fork processes, but will be kept inside the nearest blocking context. This is the essence of attachment to parent.

So parent yield call(forkedProcess) being of a blocking nature will await for return or throw resolution of child fork processes.

This is not the case with spawn(), however, since spawn is detached from enclosing parent process, i.e. attached to the root process, thus local parent doesn't have to wait.

Hope this clarifies it a bit.

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