"拆分"操作通道/处理程序



给定此操作类型:

interface SaveFoo {
type: 'SAVE_FOO'
payload: {
id: string
value: number
}
}

我想创建一个传奇故事,在选择性的基础上抑制处理程序。例如,如果发送了以下操作:

  1. { type: 'SAVE_FOO', payload: { id: "a", value: 1 } }
  2. { type: 'SAVE_FOO', payload: { id: "b", value: 1 } }
  3. { type: 'SAVE_FOO', payload: { id: "a", value: 2 } }
  4. { type: 'SAVE_FOO', payload: { id: "a", value: 3 } }

我想启动12的处理程序(因为它们具有不同的id属性(,但将34分配给缓冲区,直到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)
}
}
}
}

以下是我实现它的方式。基本上是相同的解决方案,但有点不同:

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]
}

最新更新