redux-saga実現と原理
11378 ワード
redux-sagaを紹介します.アプリを使って紹介している文章が多いですが、原理を紹介するのは本当に少ないです.自分の考えでredux-sagaの実行過程を説明します.ソースは多く削除されています.興味のあるものは自分で調べられます.
1.react-sagaとは何ですか?
redux-sagaは管理用です. Side Effects(非同期操作/副作用)のredux中間部品
2.何がレdux中間部品ですか?
redux中間部品はstore.dispatchを変えてdispatchを使用した時に副作用を処理できます.ここでは非同期動作を処理するために使用します.具体的には参考になるhttp://www.ruanyifeng.com/blo....中間の事柄に慣れていないなら、ぜひこの文章を読んでから続けてください.
3.レdux-sagaの特徴集中処理redux副作用問題 非同期は、GEnerator として実現される. watch/worker(モニター-実行)の作業形態 redux-sagaは主にsagasモードを参考にして、generatorsを使って実現しました.
まず、sagasモードについて説明します.長時間運行している事務によるシステムの運行効率と同時能力の問題を解決し、業務を複数の独立した事務に分けて、各業務は修正事務(ロールバック)を確保しています.もし業務過程が間違った状況に遭遇し、継続できない場合、修正事務を実行して完成したステップを修正できます.このようにして最終的な一致を保証します.栗を挙げます.A事務はB事務が完成してからでないと実行できません.B事務が長くかかりますと、A事務は長い間待たなければなりません.sagasモードを使えば、A事務とB事務を別々に実行できます.B事務が失敗すれば、A事務をロールバックします.
sagasモードはドキュメントを参照します.https://blog.csdn.net/ethanwh...
redux-sagaの中でsagasに対する参考:redux-sagaの中で、一つのsagaは一つのジェネレータ関数で、システム内で無期限に運行することができます.特定のactionがdispatchによって呼び覚まされる.sagaは、dispatchの追加的なactionsを継続してもよく、プログラムの単一状態ツリーにアクセスしてもよい.メインアプリケーションから起動し、一時停止とキャンセルもできます.sagasの特別な条件を満たす長い仕事を参考にして、ロールバックできます.
次に、GEnerator Generator関数はES 6が提供する非同期プログラミングソリューションである.Generator関数を実行するとエルゴードオブジェクトに戻ります.つまり、Generator関数はステータスマシン以外のエルゴードオブジェクト生成関数です.返したエルゴードオブジェクトは、Generator関数内部の各状態を順次巡回することができます.形式的には,Generator関数は一般関数であるが,二つの特徴がある.一つは、functionキーワードと関数名の間に星番号があります.第二に、関数の内部はyield表現を使って、異なる内部状態を定義します.便宜上、以下のgenはgeneratorの略称である.
簡単な例を挙げる
上の基礎を理解してから、redux-sagaの実行過程について説明します.
私たちはデモの形式で分析します.いくつかの問題を解決する必要があります.どうやって傍受執行を行いますか? はどのように循環して各ステップを実行しますか? 次のタスクはどう処理しますか? ボタンをクリックするとバックエンド要求インターフェースから戻りデータをページに更新します.
まず自分で中間部品を実現してこの需要を解決します.
ボタンをクリックしてdispatchを実行します.
上記の例に対して、私達はgen.next()で一歩ずつ実行します.初期化時にgenをtakersに追加します.このようにする目的はボタンをクリックする時にgenerator を実行することができます.ボタンをクリックした時に、GEneratorを取得して、takersから削除して、dispatchを防止します.つまりアメリカを更新する時に、再度ジェネレータを実行して、循環呼び出し をもたらします. gen.next()つまり、yield axios.post('xxx')を実行して、ここで戻ってくるのはpromise です. promise.thenでgen.nextを呼び出し、実際にはyield dispatchを呼び出します. 最後にgen.nextを呼び出し、実際にはyield tarss.pushを呼び出します.ここはゲナートをタタリに追加するために、次の前のステップの時に使います. ここでは簡単にこの需要を実現しました.redux-sagaはもっと強力なappiを提供しました.次にredux-sagaはどうやって実現されたのか見てみましょう.
まずコードの中でどうやってredux-sagaを使いますか?は、最初にredux-sagaの中間部品を導入し、実はreduxの中間部品であり、dispatch(action)を変更することによって、actionを開始する時に非同期動作を処理させる. 初期化時に一度だけサガを呼び出します.これは一回だけです.sagaを呼び出して、パイプラインのtakerを取得する方法を購読関数にプッシュして、taskを取得します.Taskインターフェースは、fork、middleare.runまたはrunSagaを介してSagaを実行する結果を指定しています. 方法
戻り値
task.is Running()
タスクがまだ戻っていない場合、またはエラーを投げた場合はtrueとなります.
task.isCancelled()
ジョブがキャンセルされたらtrueです.
task.result()
タスクの戻り値.タスクがまだ実行中の場合は
task.error()
ミッション投げのエラー.タスクが実行中である場合は
task.done
一つのPromiseは、次の二つのうちの一つである.1.任務の戻り値である.2.revoveは任務で投げ出したエラーrejectである.
task.cel()
ジョブをキャンセルします.(まだ実行中の場合)ユーザ情報を取得するボタンをクリックします.私たちはsagaミドルウェアに加入していますので、store.dispatchを開始します.
次はsaga中間部品の主要コードです.
だから、ここで実行しているのはパイプの中のtakeを取得することです.
ここでソースを実行します.
ここでソースを実行します.
以下に実行フローチャートを2枚添付します. サガ初期化 dispatch ここでは実行の流れといくつかのアプリだけを説明します.もっと多いのはドキュメントを参照してください.https://redux-saga-in-chinese...
本論文では、いくつかのeffect方法を簡単に実現し、redux-sagaの原理を紹介します.本当にredux-sagaのすべての機能を実現するには、もう少し詳細を追加するだけでいいです.
上记のソースはすべて削除版で、自分でソースコードを调べられます.
1.react-sagaとは何ですか?
redux-sagaは管理用です. Side Effects(非同期操作/副作用)のredux中間部品
2.何がレdux中間部品ですか?
redux中間部品はstore.dispatchを変えてdispatchを使用した時に副作用を処理できます.ここでは非同期動作を処理するために使用します.具体的には参考になるhttp://www.ruanyifeng.com/blo....中間の事柄に慣れていないなら、ぜひこの文章を読んでから続けてください.
3.レdux-sagaの特徴
まず、sagasモードについて説明します.長時間運行している事務によるシステムの運行効率と同時能力の問題を解決し、業務を複数の独立した事務に分けて、各業務は修正事務(ロールバック)を確保しています.もし業務過程が間違った状況に遭遇し、継続できない場合、修正事務を実行して完成したステップを修正できます.このようにして最終的な一致を保証します.栗を挙げます.A事務はB事務が完成してからでないと実行できません.B事務が長くかかりますと、A事務は長い間待たなければなりません.sagasモードを使えば、A事務とB事務を別々に実行できます.B事務が失敗すれば、A事務をロールバックします.
sagasモードはドキュメントを参照します.https://blog.csdn.net/ethanwh...
redux-sagaの中でsagasに対する参考:redux-sagaの中で、一つのsagaは一つのジェネレータ関数で、システム内で無期限に運行することができます.特定のactionがdispatchによって呼び覚まされる.sagaは、dispatchの追加的なactionsを継続してもよく、プログラムの単一状態ツリーにアクセスしてもよい.メインアプリケーションから起動し、一時停止とキャンセルもできます.sagasの特別な条件を満たす長い仕事を参考にして、ロールバックできます.
次に、GEnerator Generator関数はES 6が提供する非同期プログラミングソリューションである.Generator関数を実行するとエルゴードオブジェクトに戻ります.つまり、Generator関数はステータスマシン以外のエルゴードオブジェクト生成関数です.返したエルゴードオブジェクトは、Generator関数内部の各状態を順次巡回することができます.形式的には,Generator関数は一般関数であるが,二つの特徴がある.一つは、functionキーワードと関数名の間に星番号があります.第二に、関数の内部はyield表現を使って、異なる内部状態を定義します.便宜上、以下のgenはgeneratorの略称である.
簡単な例を挙げる
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
実行結果hw.next()
// { value: 'hello', done: false }
hw.next()
// { value: 'world', done: false }
hw.next()
// { value: 'ending', done: true }
hw.next()
// { value: undefined, done: true }
もう少し複雑な伝値の例をください.function* foo(x) {
var y = 2 * (yield (x + 1));
var z = yield (y / 3);
return (x + y + z);
}
var b = foo(5);
実行結果b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false }
b.next(13) // { value:42, done:true }
具体的な参考文献http://es6.ruanyifeng.com/#do...ここが重要です.ご了承ください.上の基礎を理解してから、redux-sagaの実行過程について説明します.
私たちはデモの形式で分析します.いくつかの問題を解決する必要があります.
まず自分で中間部品を実現してこの需要を解決します.
import axios from 'axios';
const takers = []; // generator
// gen
function runGen(dispatch, gen) {
//
if (!takers.length) {
return;
}
// generator
takers.forEach((taker, key) => {
const g = taker();
// taker
takers.splice(key, 1);
// yield axios.post promise
const userPromise = g.next().value;
userPromise.then((user) => {
// {dispatch, user} , yield dispatch
g.next({ dispatch, user });
// yield takers
g.next()
});
})
}
export default function fetchMiddleware(_ref2) {
var getState = _ref2.getState,
dispatch = _ref2.dispatch;
// taker, generator takers, dispatch
fetchMiddleware.run = () => takers.push(gen)
// dispatch
return (next) => {
return (action) => {
// dispatch
var result = next(action);
runGen(dispatch, gen)
return result;
};
};
return fetchMiddleware;
}
どう使いますかimport fetchMiddleware from './fetchMiddleware';
//
fetchMiddleware.run()
// gen
function* gen(){
const { dispatch, user } = yield axios.post('http://rest.learncode.academy/api/wetern/users');
const { data } = user;
yield dispatch({ type: 'UPDATE_USER', payload: data })
yield takers.push(gen)
}
今からこの中間部品の実現手順を見てみましょう.ボタンをクリックしてdispatchを実行します.
上記の例に対して、私達はgen.next()で一歩ずつ実行します.
まずコードの中でどうやってredux-sagaを使いますか?
./index.js
import { put, call, take } from 'redux-saga/effects';
import { createStore, applyMiddleware } from 'redux';
import createSagaMiddleware from 'redux-saga';
// action
const action = type => store.dispatch({ type })
// redux-saga
const sagaMiddleware = createSagaMiddleware()
// store
const store = createStore(rootReducer, applyMiddleware(sagaMiddleware))
// redux-saga
sagaMiddleware.run(rootSaga)
function render() {
ReactDOM.render(
action('FETCH_USER')}
/>,
document.getElementById('root'),
)
}
./saga.js
// saga
export function* rootSaga() {
while(true) {
yield take('FETCH_USER');
const { data } = yield call(axios.post, 'http://rest.learncode.academy/api/wetern/users');
yield put({ type: 'UPDATE_USER', payload: data })
}
}
function sagaMiddleware(_ref2) {
var getState = _ref2.getState,
dispatch = _ref2.dispatch;
// next = store.dispatch redux
return function (next) {
return function (action) {
// dispatch(action) dispatch
var result = next(action); // hit reducers
// emitter ,subscribe /emit
sagaEmitter.emit(action);
return result;
};
};
}
//
export function emitter() {
var subscribers = [];
// ,
function subscribe(sub) {
subscribers.push(sub);
return function () {
return remove(subscribers, sub);
};
}
// , , dispatch
function emit(item) {
var arr = subscribers.slice();
for (var i = 0, len = arr.length; i < len; i++) {
arr[i](item);
}
}
}
dispatchの時に購読器の関数を実行しますが、購読器の中の関数は何ですか?私達は第二歩を見ます.戻り値
task.is Running()
タスクがまだ戻っていない場合、またはエラーを投げた場合はtrueとなります.
task.isCancelled()
ジョブがキャンセルされたらtrueです.
task.result()
タスクの戻り値.タスクがまだ実行中の場合は
undefined
です.task.error()
ミッション投げのエラー.タスクが実行中である場合は
undefined
である.task.done
一つのPromiseは、次の二つのうちの一つである.1.任務の戻り値である.2.revoveは任務で投げ出したエラーrejectである.
task.cel()
ジョブをキャンセルします.(まだ実行中の場合)
export function runSaga(storeInterface, saga) {
// iterator rootSaga
var task = proc(iterator, subscribe, wrapSagaDispatch(dispatch), getState, context,
{ sagaMonitor: sagaMonitor, logger: logger, onError: onError }, effectId, saga.name);
return task;
}
次に、プロシージャで何が起こったかを見てみます.上のiteratorはroot Sagaというgenerator関数のパッケージです.procでredux-sagaはgen.nextを実行します.私たちのyield take('FETCHuUSER');その後、value={take:{pattern:"FETCHuUSER"}に戻ります.戻り値によって、runTake Effect()関数が実行されると判断します.この関数では、takeを登録して、nextをパイプのtakeに追加します.ここで呼び出しを終了し、taskに戻ります.export default function proc(iterator) {
// taker (chan.put)push subscribers, chan.put(input)
var stdChannel = _stdChannel(subscribe);
// task
var task = newTask(parentEffectId, name, iterator, cont);
next();
function next(arg, isErr) {
var result = void 0;
// iterator rootSaga, yield take('FETCH_USER');
// value={TAKE:{pattern: "FETCH_USER"}}
result = iterator.next(arg);
// value,
runEffect(result.value, parentEffectId, '', next);
}
}
// cb next
function runTakeEffect(_ref2, cb) {
var channel = _ref2.channel,
pattern = _ref2.pattern,
maybe = _ref2.maybe;
channel = channel || stdChannel;
var takeCb = function takeCb(inp) {
return cb(inp);
};
// taker, next takers
channel.take(takeCb, matcher(pattern));
}
次はsaga中間部品の主要コードです.
// next = store.dispatch redux
return function (next) {
return function (action) {
// dispatch(action)
var result = next(action); // hit reducers
//
sagaEmitter.emit(action);
return result;
};
};
次はsagaEmitter.emiit(action)が何をするか見てみましょう.//
export function emitter() {
...
//
function emit(item) {
var arr = subscribers.slice();
for (var i = 0, len = arr.length; i < len; i++) {
arr[i](item);
}
}
}
ここでは、購読関数を巡回して、購読関数の方法を実行します.初期化されたのは、私たちはすでにパイプラインの中でtakerを取得する方法を購読関数にプッシュしました.だから、ここで実行しているのはパイプの中のtakeを取得することです.
function put(input) {
...
for (var i = 0; i < takers.length; i++) {
var cb = takers[i];
if (!cb[MATCH] || cb[MATCH](input)) {
takers.splice(i, 1); // taker
return cb(input); // cb next
}
}
}
初期化実行中のyield taneでは、すでにgen.nextをtakersに入れています.ここでcbは実際にgen.nextを実行しています.初期化時にはgen.next関数は一回のgen.nextを実行しています.http://rest.learncode.academy...と同時に{type:FETCHuUSER}は前のステップの値として伝えられます.yeild callを実行してvalue{CALL:{args:[url]}に戻ります.戻り値によって、ここでソースを実行します.
function runCallEffect(_ref4, effectId, cb) {
const result = fn.apply(context, args);
// promise
return resolvePromise(result, cb);
}
function resolvePromise(promise, cb) {
// cb gen.next, yield call gen.next
promise.then(cb, function (error) {
return cb(error, true);
});
}
続いてgen.nextが実行したのはyield put({type:''UPDATEuUSER'、payload:data}で、実行したリターン値value{PUT:{action:{payload:}、type:[UPDATEuUSER])は、戻り値によって、ここでソースを実行します.
function runPutEffect(_ref3, cb) {
var channel = _ref3.channel,
action = _ref3.action;
asap(function () {
var result = (channel ? channel.put : dispatch)(action); // , dispatch(action)
return cb(result);
});
}
dispatch(action)を実行すると、ここで中間部品に戻り、再び第三段階の開始過程に入ります.そして更新が完了しました.今回はtarsを遍歴したところに行きます.tarsはすでに空の配列になりました.直接returnします.これで全体の取得インターフェースが更新されました.while(true)サイクルのため、再びyield tane Tale(「FETCHuUSER」)を実行します.以下に実行フローチャートを2枚添付します.
本論文では、いくつかのeffect方法を簡単に実現し、redux-sagaの原理を紹介します.本当にredux-sagaのすべての機能を実現するには、もう少し詳細を追加するだけでいいです.
上记のソースはすべて削除版で、自分でソースコードを调べられます.