本人正在做的項目遇到這樣的一個需求,需要每隔一段時間(例如1分鐘)就要從服務(wù)器拉取數(shù)據(jù)彈出新信息贪婉,我選擇了用輪詢的方法去做衔掸,為什么不用websocket推送,因為開發(fā)時間關(guān)系--
很快就寫了主要邏輯济炎,路由監(jiān)聽在非登錄頁的地方進(jìn)行第一次調(diào)用(配合flag,防止多次調(diào)用)辐真,在函數(shù)內(nèi)利用delay阻塞须尚,時間過后再往下調(diào)用接口崖堤、執(zhí)行代碼,然后再調(diào)用相同函數(shù)恨闪,以此達(dá)到一定時間間隔的調(diào)用倘感。代碼如下:
// app.js
...
effects: {
*getRemindingMessage({ payload }, { call, put }) {
// 延遲一定時間
yield delay(REMIND_TIME)
const res = yield call(getRemindingMessage, payload)
if (res.state === '1') {
...
// 再去調(diào)用
yield put({
type: 'getRemindingMessage',
payload: {},
})
}
}
}
...
subscriptions: {
setupHistory({ dispatch, history }) {
history.listen(location => {
...
// 第一次開始的地方
if (history.location.pathname !== '/login' && firstNotify) {
firstNotify = false
dispatch({
type: 'getRemindingMessage',
payload: {}
})
}
})
}
}
然后無意中發(fā)現(xiàn)了問題,上圖
看到nprogress一直是loading狀態(tài)咙咽,查看了Redux DevTools老玛,發(fā)現(xiàn)
很快定位到問題,effects里的getRemindingMessage函數(shù)有delay钧敞,它一直在函數(shù)里阻塞蜡豹,導(dǎo)致這個effects一直是loading狀態(tài),從而nprogress一直是loading溉苛,但我們希望nprogress的作用僅僅是反應(yīng)接口的響應(yīng)情況镜廉,而不是還得顧及定時器的處理邏輯。
那我就不用同步(yield)的寫法了,嘗試用setTimeout回調(diào)
看來定時器里不能寫yield,現(xiàn)在又需要有計時的邏輯蜕窿,忽然想到用Web Worker,就用它來幫我計時吧
先寫一個worker的生成函數(shù)塔插,方便復(fù)用
// utils/index.js
// 生成web worker
export function createWorker(f) {
let code = f.toString()
code = code.substring(code.indexOf('{') + 1, code.lastIndexOf('}'))
const blob = new Blob([code], { type: 'application/javascript'})
const url = URL.createObjectURL(blob)
return new Worker(url)
}
接著寫計時worker
onmessage用來監(jiān)聽主線程發(fā)過來的時機,等待主線程傳來的參數(shù)(時間)用來計時拓哟,到一定時間告訴主線程要做動作了
// utils/timerWorker.js
export default function () {
self.onmessage = function(e) {
setTimeout(() => {
self.postMessage(null)
}, e.data)
}
}
主線程邏輯想许,將delay干掉
【1】主:先new一個worker(封裝在createWoker),我把時間(1分鐘)告訴給worker
【2】子:收到時間(1分鐘)断序,我開始倒計時流纹,時間到了再告訴你。违诗。漱凝。時間到了
【3】主:收到你的提醒,我要執(zhí)行方法了
【4】方法:調(diào)用接口诸迟,根據(jù)返回做完處理動作了茸炒,我告訴子線程重新計時吧
【5】子:收到時間(1分鐘),我開始倒計時亮蒋。扣典。妆毕。然后跳到【3】
// app.js(修改后)
...
import { createWorker } from 'utils'
import worker from 'utils/timerWorker'
...
// 定義一個變量準(zhǔn)備給worker實例
let myWorker = null
...
effects: {
*getRemindingMessage({ payload }, { call, put }) {
// 延遲一定時間慎玖,刪掉
// yield delay(REMIND_TIME)
const res = yield call(getRemindingMessage, payload)
if (res.state === '1') {
...
// 再去調(diào)用,改為告訴worker要倒計時了
// yield put({
// type: 'getRemindingMessage',
// payload: {},
// })
myWorker.postMessage(REMIND_TIME)
}
}
}
...
subscriptions: {
setupHistory({ dispatch, history }) {
history.listen(location => {
...
// 第一次開始的地方
if (history.location.pathname !== '/login' && firstNotify) {
firstNotify = false
myWorker = createWorker(worker)
// 帶著時間告訴worker笛粘,目的是告訴子線程可以開始在這個時間后再告訴我了
myWorker.postMessage(REMIND_TIME)
// 一旦worker子線程告訴我可以趁怔,我就可以執(zhí)行里面的代碼
myWorker.onmessage = function(e) {
dispatch({
type: 'getRemindingMessage',
payload: {}
})
}
}
if (history.location.pathname === '/login') {
// 別忘了做清除工作湿硝,記得銷毀worker
myWorker && myWorker.terminate()
}
})
}
}
修改完,觀察Redux DevTools润努,沒毛病关斜,loading沒被定時器阻塞