您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)redux-saga原理是什么,小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。
Generator函數(shù)的自動(dòng)流程控制
在redux-saga中,saga是指一些長時(shí)操作,用generator函數(shù)表示。generator函數(shù)的強(qiáng)大之處在于其可以手動(dòng)的暫停、恢復(fù)執(zhí)行,且可以與函數(shù)體外進(jìn)行數(shù)據(jù)交互,看如下例子:
function *gen() { const a = yield 'hello'; console.log(a); } cont g = gen(); g.next(); // { value: 'hello', done: false } setTimeout(() => g.next('hi'), 1000) // 此時(shí) a => 'hi' 一秒后打印‘hi'
可以看出來genrator函數(shù)何時(shí)進(jìn)行下一步操作完全取決于外部的調(diào)度時(shí)機(jī),且其內(nèi)部執(zhí)行狀態(tài)也由外部的輸入決定,這使得generator函數(shù)可以很方便的做異步流程控制。舉個(gè)例子,我們首先讀取一個(gè)文件的內(nèi)容作為查詢參數(shù),然后請求一個(gè)查詢接口并把返回的內(nèi)容打印出來:
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); }
我們可以手動(dòng)控制gen函數(shù)的執(zhí)行:
const g = gen(); g.next().value.then(params => { g.next(params).value.then(content => { g.next(content); }) })
以上可以達(dá)到我們的目的,但是過于繁瑣,我們想要的是generator函數(shù)可以自動(dòng)的執(zhí)行,可以寫一個(gè)簡易的自動(dòng)執(zhí)行函數(shù)如下:
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);
此時(shí)generator函數(shù)便可以自動(dòng)執(zhí)行,事實(shí)上我們可以發(fā)現(xiàn),generator的內(nèi)部狀態(tài)完全是由nextWithYieldType
決定的,我們可以根據(jù)yield的類型執(zhí)行不同的處理邏輯。
Effect
事實(shí)上sagaMiddleware.run(saga)可以類似看做genRun(saga),而saga是由一個(gè)個(gè)的effect組成的,那么effect是什么?redux-saga官網(wǎng)的解釋:一個(gè) effect 就是一個(gè) Plain Object JavaScript 對象,包含一些將被 saga middleware 執(zhí)行的指令。redux-saga提供了很多Effect創(chuàng)建器,如call、put、take等,已call為例:
function saga*() { const result = yield call(genPromise); console.log(result); }
call(genPromise)
生成的就是一個(gè)effect,它可能類似如下:
{ isEffect: true, type: 'CALL', fn: genPromise }
事實(shí)上effect只表明了意圖,而實(shí)際的行為由類似于上文的nextWithYieldType完成,例如:
function nextWithYieldType(value, next) { ... if(isCallEffect(value)) { value.fn(). then(success => next(null, success)).catch(error => next(error)) } }
當(dāng)genPromise函數(shù)返回的promise被resolve后便會(huì)打印出結(jié)果。
生產(chǎn)者與消費(fèi)者
觀察下面的例子
function *saga() { yield take('TEST'); console.log('test...'); } sagaMiddleware.run(test);
saga會(huì)在take('TEST')處阻塞,只有執(zhí)行了dispatch({type: 'TEST'})后saga才能繼續(xù)運(yùn)行(注意:此時(shí)的dispatch方法是經(jīng)過sagaMiddleware包裝過的)。這給我們的感覺似乎很像是take是一個(gè)生產(chǎn)者,在等待disaptch的消費(fèi),事實(shí)上take只是一個(gè)Effect生成器,具體的處理邏輯依然是在nextWithYieldType完成的,類似于:
function nextWithYieldType(value, next) { ... // take('TEST')生成的effect簡單的認(rèn)為是 {isEffect: true, type: 'TAKE', name: 'TEST'} if(isTakeEffect(value)) { channel.take({pattern: value.name, cb: params => next(null, params)}) } }
channel是一個(gè)任務(wù)生成器,它有兩個(gè)方法:take生成任務(wù),put消費(fèi)任務(wù):
function channel() { /* task = { pattern, cb } */ let _task = null; function take(task) { _task = task; } function put(pattern, args) { if(!_task) return; if(pattern == _task.pattern) _task.cb.call(null, args); } return { take, put } }
顯然任務(wù)是在執(zhí)行dispatch的時(shí)候被消費(fèi)掉的,這個(gè)工作是在sagaMiddleware中做的,類似于如下:
const sagaMiddleware = store => { return next => action => { next(action); const { type, ...payload } = action; channel.put(type, payload); } }
關(guān)于redux-saga原理是什么就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。