この記事の内容は redux-saga 原則の解釈に関するものです (コード例)。必要な方は参考にしていただければ幸いです。
著者は最近、redux-saga を使用して非同期データ フローを処理する Ant Design Pro を使用したいくつかのバックエンド プロジェクトに取り組んでいます。この記事では redux-saga の原理を簡単に解釈し、実装します。 a redux-saga の簡易バージョン。
ジェネレーター関数の自動プロセス制御
redux-saga では、saga はジェネレーター関数で表されるいくつかの長期的な操作を指します。ジェネレーター関数の利点は、実行を手動で一時停止および再開でき、関数本体の外部でデータを操作できることです。次の例を参照してください。
function *gen() { const a = yield 'hello'; console.log(a); } cont g = gen(); g.next(); // { value: 'hello', done: false } setTimeout(() => g.next('hi'), 1000) // 此时 a => 'hi' 一秒后打印‘hi'
ジェネレーター関数が次の処理を実行すると、それがわかります。動作は外部のスケジューリングタイミングに完全に依存し、内部の実行ステータスも外部入力によって決定されるため、ジェネレータ機能は非同期プロセス制御に便利です。たとえば、最初にファイルの内容をクエリ パラメータとして読み取り、次にクエリ インターフェイスをリクエストして、返された内容を出力します:
function getParams(file) { return new Promise(resolve => { fs.readFile(file, (err, data) => { resolve(data) }) }) } function getContent(params) { // request返回promise return request(params) } function *gen() { const params = yield getParams('config.json'); const content = yield getContent(params); console.log(content); }
gen 関数の実行を手動で制御できます:
const g = gen(); g.next().value.then(params => { g.next(params).value.then(content => { g.next(content); }) })
上記で目的は達成できますが、面倒すぎるので、ジェネレーター関数を自動実行できるようにしたいのですが、次のように簡単な自動実行関数を記述します。実際、ジェネレーター関数は
nextWithYieldType によって完全に決定され、yield のタイプに応じて異なる処理ロジックを実行できることがわかります。
実際、sagaMiddleware.run(saga) は genRun(saga) と同様とみなすことができ、saga はエフェクトで構成されており、そのエフェクトは次のようになります。何? redux-saga公式サイトの説明:一 エフェクトは、saga ミドルウェアによって使用されるいくつかの要素を含むプレーン オブジェクト JavaScript オブジェクトです。 実行する命令。 redux-saga は、call、put、take などの多くのエフェクト クリエーターを提供します。call を例に挙げます。
function genRun(gen) { const g = gen(); next(); function next(err, pre) { let temp; (err === null) && (temp = g.next(pre)); (err !== null) && (temp = g.throw(pre)); if(!temp.done) { nextWithYieldType(temp.value, next); } } } function nextWithYieldType(value, next) { if(isPromise(value)) { value .then(success => next(null, success)) .catch(error => next(error)) } } genRun(gen);
は、次のようなエフェクトを生成します。以下: <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">function saga*() {
const result = yield call(genPromise);
console.log(result);
}</pre><div class="contentsignin">ログイン後にコピー</div></div>
実際、この効果は意図を示すだけであり、実際の動作は上記と同様の nextWithYieldType によって完了します。たとえば:
{ isEffect: true, type: 'CALL', fn: genPromise }
When the Promise returns by the genPromise関数が解決されると、結果が表示されます。
プロデューサーとコンシューマー次の例を確認してください。
function nextWithYieldType(value, next) { ... if(isCallEffect(value)) { value.fn(). then(success => next(null, success)).catch(error => next(error)) } }
saga は take('TEST') でブロックされ、ディスパッチのみが実行されます ({type : 'TEST'}) を実行してから、saga が実行を続行できるようにします (注: この時点のディスパッチ メソッドは sagaMiddleware によってパッケージ化されています)。これは、take がディスパッチの消費を待っているプロデューサーであるように感じられます。実際、take は単なる Effect ジェネレーターであり、特定の処理ロジックは次のように nextWithYieldType で完了します。
function *saga() { yield take('TEST'); console.log('test...'); } sagaMiddleware.run(test);
channel はタスクです。ジェネレーターには 2 つのメソッドがあります: タスクを生成する take とタスクを消費する put :
function nextWithYieldType(value, next) { ... // take('TEST')生成的effect简单的认为是 {isEffect: true, type: 'TAKE', name: 'TEST'} if(isTakeEffect(value)) { channel.take({pattern: value.name, cb: params => next(null, params)}) } }
この作業は、次のように sagaMiddleware で実行されます。
ここを見ると、nextWithYieldType 関数を継続的に改善する必要があることがわかります。put、fork、takeEvery に対応するロジックが完成した後、基本的な機能を備えた redux が誕生したので、ここでは説明しません。これらの関数の実装について詳しく説明します。以上がredux-saga 原則の解釈 (コード例)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。