2017-08-06 56 views
1

我试图一次只处理一个给定的ID的下载。我希望它是非阻塞的,所以“不同的文件ID”可以并行下载,这就是为什么我在做takeEvery。我想放弃,如果“相同的文件ID”已在进行中。takeEvery,但它是丢弃每一个

一旦行动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; 
    } 
} 

回答

1

这是因为当动作被分派时总是最先执行减速,然后才传奇。解决这个问题的一种方法是将流量分成两个操作。 One - DOWNLOAD_FILE - 开始传奇,然后第二个 - - DOWNLOAD_FILE_STARTED从佐贺isDownloading是假的,并更新isDownloading状态时被分派:

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状态之间的一些国家应该有正确的值。

+0

非常感谢Martin对你的帮助。我希望避免通过'DOWNLOAD_FILE_STARTED'创建另一个动作,因为我想使用'DOWNLOAD_FILE' - 因为现在该动作被reducer忽略,我想很好地使用它,这是不可能的? – Noitidart

+0

我想到另一种方式来做到这一点,我更新了我的答案,包括其他解决方案:) –

+0

非常酷,谢谢你马丁!所以'watcher'总是在'worker'之前触发的'reducer'之前触发?这是生命周期吗? – Noitidart