我试图一次只处理一个给定ID的下载。我想要它非阻塞,所以"不同的文件ID"可以并行下载,这就是为什么我在做takeEvery
。我想丢弃一个"相同的文件ID"已经在进行中。
一旦调度了动作DOWNLOAD_FILE
,我就在减速器中设置file.isDownloading = true
。然而,downloadFileWorker
总是发现它是真实的并且丢弃。
这是我的传奇:
const DOWNLOAD_FILE = 'DOWNLOAD_FILE';
export function downloadFile(id) {
return {
type: DOWNLOAD_FILE,
id
}
}
function* downloadFileWorker(action) {
const { id } = action;
const state = yield select();
const files = state;
const file = files.find(file => file.id === id);
if (file.isDownloading) {
console.log('discontinuing worker as file is already downloading');
return;
} else {
console.log('OK CONTINUE TO DOWNLOAD');
const res = yield call(fetch, ...);
const json = yield call(res.json);
console.log('json:', json);
}
}
function* downloadFileWatcher() { // i think i can call this downloadFileSaga
yield takeEvery(DOWNLOAD_FILE, downloadFileWorker);
}
我们在这里看到这首先找到状态中的文件,如果它是isDownloading
,那么它就不会继续(在downloadFileWorker
中)。
当触发DOWNLOAD_FILE
操作时,在reducer中我将文件设置为isDownloading
,因此我的工作人员总是丢弃。这是我的减速机:
export default function reducer(state=INITIAL, action) {
switch(action.type) {
case DOWNLOAD_FILE: {
const { id } = action;
const files = state;
if (!files) return state;
const file = files.find(file => file.id === id);
if (!file) return state;
if (file.isDownloading) {
console.log('discarding reducer action, as file is already downloading');
return state;
}
return files.map( file => file.id !== id ? file : { ...file, isDownloading:true } )
}
default: return state;
}
}
答案 0 :(得分:1)
这是因为当调度action时,reducer总是先执行,然后才执行saga。解决此问题的一种方法是将流分为两个动作。 One - DOWNLOAD_FILE - 启动传奇然后再启动 - 例如DOWNLOAD_FILE_STARTED - 当isDownloading为false并从更新isDownloading状态时从saga调度:
const DOWNLOAD_FILE = 'DOWNLOAD_FILE';
const DOWNLOAD_FILE_STARTED = 'DOWNLOAD_FILE_STARTED';
export function downloadFile(id) {
return {
type: DOWNLOAD_FILE,
id
}
}
export function downloadFileStarted(id) {
return {
type: DOWNLOAD_FILE_STARTED,
id
}
}
function* downloadFileWorker(action) {
const { id } = action;
const state = yield select();
const files = state;
const file = files.find(file => file.id === id);
if (file.isDownloading) {
console.log('discontinuing worker as file is already downloading');
return;
} else {
console.log('OK CONTINUE TO DOWNLOAD');
yield put(downloadFileStarted(id));
const res = yield call(fetch, ...);
const json = yield call(res.json);
console.log('json:', json);
}
}
function* downloadFileWatcher() { // i think i can call this downloadFileSaga
yield takeEvery(DOWNLOAD_FILE, downloadFileWorker);
}
...
export default function reducer(state=INITIAL, action) {
switch(action.type) {
case DOWNLOAD_FILE_STARTED: {
const { id } = action;
const files = state;
if (!files) return state;
const file = files.find(file => file.id === id);
if (!file) return state;
if (file.isDownloading) {
console.log('discarding reducer action, as file is already downloading');
return state;
}
return files.map( file => file.id !== id ? file : { ...file, isDownloading:true } )
}
default: return state;
}
}
如果你想避免第二个动作,另一种解决方法是记住当前和之前的动作调度之间的状态。
function* downloadFileWorker(action, prevState) {
const { id } = action;
const files = prevState;
const file = files.find(file => file.id === id);
if (file.isDownloading) {
console.log('discontinuing worker as file is already downloading');
return;
} else {
console.log('OK CONTINUE TO DOWNLOAD');
const res = yield call(fetch, ...);
const json = yield call(res.json);
console.log('json:', json);
}
}
function* downloadFileWatcher() {
while (true) {
const prevState = yield select();
const action = yield take(DOWNLOAD_FILE);
yield fork(downloadFileWorker, action, prevState);
}
}
prevState
不一定完全是先前的状态,但它应该是最后两个DOWNLOAD_FILE调度之间的某种状态,因此isDownloading状态应该具有正确的值。