Об авторе joey Ant Financial Data Experience Technology Team
был использован в проектеredux-sagaДля обработки процесса асинхронного действия. Очень любопытен принцип реализации Эффекта. Я нашел время, чтобы изучить его реализацию. В этой статье не описывается базовый API и преимущества REDUX-SAGA, простой принцип реализации чата, вы можете оставить сообщение в области комментариев.
предисловие
Код для redux-saga для отслеживания действия выглядит следующим образом:
import { takeEvery } from 'redux-saga';
function* mainSaga() {
yield takeEvery('action_name', function* (action) {
console.log(action);
});
}
Как использовать генераторtakeEvery
что о? Давайте рассмотрим немного более простойtake
Принцип реализации:
принять принцип реализации
Давайте попробуем написать демо и использовать метод саги для наблюдения за действием с генератором.
$btn.addEventListener('click', () => {
const action =`action data${i++}`;
// trigger action
}, false);
function* mainSaga() {
const action = yield take();
console.log(action);
}
быть в$btn
При нажатии можно прочитать значение действия.
channel
Здесь нужно ввести понятие -channel
.
Канал является абстракцией источника события. Функция заключается в том, чтобы сначала зарегистрировать метод take. Когда срабатывает put, выполнить метод take один раз, а затем уничтожить его.
Простая реализация канала выглядит следующим образом:
function channel() {
let taker;
function take(cb) {
taker = cb;
}
function put(input) {
if (taker) {
const tempTaker = taker;
taker = null;
tempTaker(input);
}
}
return {
put,
take,
};
}
const chan = channel();
Мы используем канал для соединения генератора и события dom и переписываем событие dom следующим образом:
$btn.addEventListener('click', () => {
const action =`action data${i++}`;
chan.put(action);
}, false);
Когда срабатывает пут, если в канале уже есть зарегистрированный тейкер, тейкер будет выполняться.
Нам нужно вызвать метод take канала, чтобы зарегистрировать фактический метод, который будет запущен до того, как сработает put.
Продолжаем смотреть на реализацию в mainSaga.
function* mainSaga() {
const action = yield take();
console.log(action);
}
Этот дубль является типом эффекта в саге.
Первый взгляд на эффектtake()
реализация.
function take() {
return {
type: 'take'
};
}
Я неожиданно только что вернул тип Object.
На самом деле, значение, возвращаемое всеми эффектами в redux-saga, является типизированным чистым объектным объектом.
Итак, когда именно срабатывает метод взятия канала? Вам также нужно найти причину в коде, который вызывает mainSaga.
Особенность генератора в том, что при выполнении определенного шага управление может быть передано внешнему коду, а внешний код может решить, что делать после получения возвращаемого результата.
task
Здесь мы вводим новое понятиеtask
.
task
Это среда выполнения метода Generator, и все методы Generator всех Saga выполняются в Task.
Простая и легкая задача выглядит следующим образом:
function task(iterator) {
const iter = iterator();
function next(args) {
const result = iter.next(args);
if (!result.done) {
const effect = result.value;
if (effect.type === 'take) {
runTakeEffect(result.value, next);
}
}
}
next();
}
task(mainSaga);
когдаyield take()
При беге,take()
Возвращенный результат передается внешней задаче, а управление кодом передается задаче от метода-генератора.
result.value
Значениеtake()
возвращенный результат{ type: 'take' }
.
посмотри сноваrunTakeEffect
Реализация:
function runTakeEffect(effect, cb) {
chan.take(input => {
cb(input);
});
}
На этом этапе мы, наконец, видим, где вызывается метод take канала.
Полный код выглядит следующим образом:
function channel() {
let taker;
function take(cb) {
taker = cb;
}
function put(input) {
if (taker) {
const tempTaker = taker;
taker = null;
tempTaker(input);
}
}
return {
put,
take,
};
}
const chan = channel();
function take() {
return {
type: 'take'
};
}
function* mainSaga() {
const action = yield take();
console.log(action);
}
function runTakeEffect(effect, cb) {
chan.take(input => {
cb(input);
});
}
function task(iterator) {
const iter = iterator();
function next(args) {
const result = iter.next(args);
if (!result.done) {
const effect = result.value;
if (effect.type === 'take') {
runTakeEffect(result.value, next);
}
}
}
next();
}
task(mainSaga);
let i = 0;
$btn.addEventListener('click', () => {
const action =`action data${i++}`;
chan.put(action);
}, false);
Общий процесс заключается в том, чтобы сначала зарегистрировать тейкера на канале через mainSaga.Как только происходит щелчок по дому, запускается пут канала, и пут будет потреблять зарегистрированного тейкера, тем самым завершая процесс мониторинга события клика.
Посмотреть онлайн-демонстрацию
принцип реализации takeEvery
В предыдущем разделе мы имитировали сагу, чтобы реализовать мониторинг событий, но все еще есть проблема, мы можем отслеживать только один клик, как мы можем отслеживать каждое событие клика? redux-saga предоставляет вспомогательный метод —takeEvery
. Попробуем реализовать это в нашей облегченной версии саги.takeEvery
.
function* takeEvery(worker) {
yield fork(function* () {
while(true) {
const action = yield take();
worker(action);
}
});
}
function* mainSaga() {
yield takeEvery(action => {
$result.innerHTML = action;
});
}
Здесь используется новый метод эффектаfork
.
fork
Роль форка заключается в том, чтобы запустить новую задачу, не блокируя выполнение исходной задачи. Код изменен следующим образом:
function fork(cb) {
return {
type: 'fork',
fn: cb,
};
}
function runForkEffect(effect, cb) {
task(effect.fn || effect);
cb();
}
function task(iterator) {
const iter = typeof iterator === 'function' ? iterator() : iterator;
function next(args) {
const result = iter.next(args);
if (!result.done) {
const effect = result.value;
// 判断effect是否是iterator
if (typeof effect[Symbol.iterator] === 'function') {
runForkEffect(effect, next);
} else if (effect.type) {
switch (effect.type) {
case 'take':
runTakeEffect(effect, next);
break;
case 'fork':
runForkEffect(effect, next);
break;
default:
}
}
}
}
next();
}
Мы добавляем новый эффектfork
, запускает новую задачу takeEvery.
Функция takeEvery состоит в том, чтобы автоматически помещать нового тейкера в канал, когда происходит пут канала.
В канале, который мы реализуем одновременно, может быть только один тейкер,while(true)
Функция состоит в том, чтобы срабатывать автоматически всякий раз, когда триггер пут потребляет тейкера.runTakeEffect
В следующем методе переданной задачи снова поставить берущего в канал, чтобы постоянно следить за событиями.
Суть эффекта
Реализуя вышеизложенное, мы обнаруживаем, что эффект доходности возвращает, в конце концов, чистый объект, используемый для выполнения задачи генератором сосуда, внешний слой посылает сигнал, сообщающий, что делать задачу.
Исходя из этой идеи, если мы хотим добавить эффект отмены задачи, это можно легко реализовать.
Сначала мы определяемcancel
способ отправки сигнала отмены.
function cancel() {
return {
type: 'cancel'
};
}
Затем измените код задачи, чтобы он действительно мог выполнять логику отмены.
function task(iterator) {
const iter = typeof iterator === 'function' ? iterator() : iterator;
...
function runCancelEffect() {
// do some cancel logic
}
function next(args) {
const result = iter.next(args);
if (!result.done) {
const effect = result.value;
if (typeof effect[Symbol.iterator] === 'function') {
runForkEffect(effect, next);
} else if (effect.type) {
switch (effect.type) {
case 'cancel':
runCancelEffect();
case 'take':
runTakeEffect(result.value, next);
break;
case 'fork':
runForkEffect(result.value, next);
break;
default:
}
}
}
}
next();
}
резюме
В этой статье представлен принцип redux-saga путем простой реализации нескольких методов эффектов.Чтобы действительно реализовать все функции redux-saga, вам нужно всего лишь добавить некоторые детали. Вероятно, как показано ниже:
Студенты, которые заинтересованы в использовании генераторов, рекомендуют изучить егоredux-saga
исходный код. Вот статья, в которой рекомендуется использовать генератор для мониторинга событий dom.Продолжайте изучать Iterator в JS и рассказывайте о сравнении с Observable.
Заинтересованные студенты могут подписаться на колонку или отправить свое резюме на 'chaofeng.lcf####alibaba-inc.com'.replace('####', '@'). Приглашаются люди с высокими идеалами~
Оригинальный адрес:GitHub.com/proto team/no…