Какая разница между вилкой и нерестится в редукс-саге?
docs говорят, что fork - прикрепленная вилка, а spawn - отдельная вилка - как они отличаются?
Ответы
Ответ 1
Один из способов взглянуть на это - увидеть вашу сагу в виде графика. 'fork' создает дочерний элемент из вызывающего процесса. В то время как "spawn" создает нового потомка в корне графа.
Поэтому, когда вы "разветвляете" другой процесс, родительский процесс будет ждать, пока "разветвленный" процесс не будет завершен. Также каждое исключение будет передаваться от ребенка к родителю и может быть перехвачено родителем.
"Порожденный" процесс не блокирует родительский процесс, поэтому следующий оператор yield вызывается немедленно. Также родительский процесс не сможет перехватывать какие-либо исключения, которые происходят в "порожденном" процессе.
Я надеюсь, что это было полезно.
Ответ 2
В тех же документах говорится:
Когда родитель завершает выполнение своего собственного тела инструкций, он будет ожидать завершения всех разветвленных задач перед возвратом.
Допустим, у нас есть такая установка, где в середине потока выполнения мы вызываем fetchAll()
которая может вызывать fork
или 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
Мы видим, что в контексте call
ответвление non-blocking
, но call
не завершится, пока не завершатся все его дочерние процессы, поскольку сам call
является блокирующим эффектом.
То же самое вы не увидите, если вызовете fork
в другом fork
, так как сам разветвление неблокирует, и внутренние разветвленные процессы будут leak
из процессов внешнего разветвления, но будут храниться в ближайшем контексте блокировки. Это суть attachment to parent
.
Поэтому родительский yield call(forkedProcess)
, имеющий блокирующую природу, будет ожидать throw resolution
return
или throw resolution
дочерних процессов разветвления.
Однако это не относится к spawn()
, так как spawn отделен от включающего родительского процесса, то есть присоединен к корневому процессу, поэтому локальный parent
не должен ждать.
Надеюсь, это прояснит это немного.