给出此操作类型:
interface SaveFoo {
type: 'SAVE_FOO'
payload: {
id: string
value: number
}
}
我想创建一个有选择地限制处理程序的传奇。因此,例如,如果调度了以下操作:
{ type: 'SAVE_FOO', payload: { id: "a", value: 1 } }
{ type: 'SAVE_FOO', payload: { id: "b", value: 1 } }
{ type: 'SAVE_FOO', payload: { id: "a", value: 2 } }
{ type: 'SAVE_FOO', payload: { id: "a", value: 3 } }
我想启动1
和2
的处理程序(因为它们具有不同的id
属性),但是将3
和4
分配给缓冲区直到1
完成处理为止。
感觉这应该是一个很普遍的用例,但是我找不到任何相关的东西。我尝试过手动执行,但是我觉得必须有更好的方法:
export function splitThrottle<T>(actionCreator: ActionCreator<T>, saga: (action: Action<T>) => SagaIterator, selector: (payload: T) => string) {
const tasks: Record<string, Task> = {}
const bufferLookup: Record<string, Buffer<Action<T>>> = {}
function* queue(action: Action<T>, id: string) {
try {
yield call(saga, action)
} catch (e) {
// don't propagate
}
const next = bufferLookup[id].take()
if (next) {
tasks[id] = yield call(queue, next, id)
} else {
delete tasks[id]
}
}
return function* () {
while (true) {
const action: Action<T> = yield take(actionCreator)
const id = selector(action.payload)
const existingTask = tasks[id]
if (existingTask) {
bufferLookup[id].put(action)
} else {
let buffer = bufferLookup[id]
if (!buffer) {
buffer = buffers.sliding(1)
bufferLookup[id] = buffer
}
tasks[id] = yield fork(queue, action, id)
}
}
}
}
答案 0 :(得分:1)
这就是我实现它的方式。几乎相同的解决方案,但有一些不同的原语:
export function* splitThrottle(pattern, saga, selector) {
const channels = {}
while (true) {
const action = yield take(pattern)
const id = selector(action)
const { channel, justCreated } = obtainChannel(channels, id)
yield put(channel, action)
if (justCreated) {
yield fork(processAllAndDelete, channels, id, saga)
}
}
}
function obtainChannel(channels, id) {
let channel = channels[id]
if (channel) {
return { channel, justCreated: false }
} else {
channel = channels[id] = channel(buffers.expanding(1))
return { channel, justCreated: true }
}
}
function* processAllAndDelete(channels, id, saga) {
const channel = channels[id]
while (true) {
const actions = yield flush(channel)
if (!actions)
break
for (const action of actions) {
yield call(saga, action)
}
}
delete channels[id]
}