如何对按ID分组的结构体Map执行防抖?当前仅输出最后一条数据
问题分析
你写的代码只输出最后一条消息,核心原因有两个:
- 循环变量捕获陷阱:Go的for循环迭代变量是复用的,闭包会引用变量的内存地址而非当前循环的具体值。所有传入防抖函数的闭包最终都指向循环结束后的最后一个
message实例。 - 防抖函数复用冲突:所有消息共用同一个防抖定时器,每次调用防抖函数都会重置延迟时间,前面的调用全部被覆盖,最终只执行最后一次传入的函数。
解决方案
如果你的需求是按ID独立防抖(同一ID的连续触发只执行最后一次,不同ID互不干扰),可以这么改:
1. 为每个ID创建独立的防抖函数
用一个map存储每个ID对应的防抖函数,确保不同ID的防抖逻辑相互隔离。
2. 修复循环变量捕获问题
在循环内部创建临时变量,保存当前迭代的message值,避免闭包引用同一个变量地址。
完整代码示例:
package main import ( "fmt" "time" ) // 自定义防抖函数实现(如果没有第三方防抖库可以直接用这个) func debounce(delay time.Duration) func(func()) { var timer *time.Timer return func(f func()) { if timer != nil { timer.Stop() } timer = time.AfterFunc(delay, f) } } type Message struct { id int attrs []string } func main() { messages := []Message{ {id: 10, attrs: []string{"blah", "foo"}}, {id: 10, attrs: []string{"add", "ddsds"}}, {id: 11, attrs: []string{"foo", "bar"}}, {id: 12, attrs: []string{"xyz"}}, } // 按ID分组 collections := make(map[int][]Message) for _, j := range messages { collections[j.id] = append(collections[j.id], j) } // 为每个ID创建独立的防抖函数 idDebouncers := make(map[int]func(func())) delay := 100 * time.Millisecond for id := range collections { idDebouncers[id] = debounce(delay) } // 遍历分组消息,用对应ID的防抖函数处理 for _, messageList := range collections { for _, message := range messageList { msg := message // 临时变量捕获当前循环的消息值 debouncer := idDebouncers[msg.id] debouncer(func() { fmt.Println("id", msg.id, "attrs =>", msg.attrs) }) } } time.Sleep(500 * time.Millisecond) }
输出结果
id 10 attrs => [add ddsds] id 11 attrs => [foo bar] id 12 attrs => [xyz]
进阶:同一ID的消息合并处理
如果你的需求是把同一ID的多条消息在防抖时间内的attrs合并后输出,而非仅执行最后一条,可以修改防抖逻辑实现累积合并:
// 支持合并属性的防抖函数 func debounceMerge(delay time.Duration, id int, output func([]string)) func([]string) { var timer *time.Timer var mergedAttrs []string return func(attrs []string) { mergedAttrs = append(mergedAttrs, attrs...) if timer != nil { timer.Stop() } timer = time.AfterFunc(delay, func() { output(mergedAttrs) mergedAttrs = nil // 重置,准备下一次累积 }) } } // 在main函数中替换防抖函数部分: func main() { // ...(前面的分组逻辑不变) // 为每个ID创建合并型防抖函数 idMergeDebouncers := make(map[int]func([]string)) for id := range collections { currentID := id // 捕获当前ID idMergeDebouncers[id] = debounceMerge(delay, currentID, func(attrs []string) { fmt.Println("id", currentID, "merged attrs =>", attrs) }) } // 遍历消息,传入属性进行合并 for _, messageList := range collections { for _, message := range messageList { idMergeDebouncers[message.id](message.attrs) } } time.Sleep(500 * time.Millisecond) }
进阶输出结果
id 10 merged attrs => [blah foo add ddsds] id 11 merged attrs => [foo bar] id 12 merged attrs => [xyz]
内容的提问来源于stack exchange,提问作者Johelvis Guzman
相关产品推荐
相关产品推荐

