0.前言
回调函数是一种在编程中常见的技术,通常在异步编程中使用。简单来说,回调函数是一个被传递给另一个函数的函数,它在该函数的某个时间点被调用,以完成某些特定的操作或任务。
在Go语言中,可以将函数直接作为参数传递给另一个函数,并在需要时被调用,这样大大的加强了代码的可定制化,但是也一定程度上减少了代码的可读性,所以在实际使用的时候要学会取舍。
1.使用
- 异步编程:通常情况下,回调函数在异步操作完成后被调用,以便通知调用方该操作已完成或返回异步操作的结果,回调函数可定制化,所以可以做不同的一些工作。
- 如果某种逻辑,有几种实现方法,那么可以使用回调函数,将实现和逻辑解耦,提高可复用性和可维护性。例如简易计算器和sort.Slice函数(在下面有代码演示)。
2.示例demo
2.1 异步访问URL
首先定义type callback func(data []byte, err error)作为回调函数的类型,随后写读取URL的逻辑(fetch函数),在主函数中,传入回调函数和URL,并通过channel异步地调用此函数,通过WaitGroup等待协程的返回,其中回调函数定义了读取的内容如何使用,是可定制的。
package mainimport ("fmt""io/ioutil""net/http""sync""time"
)type callback func(data []byte, err error)
var wg sync.WaitGroup
func fetch(url string, c callback) {go func() {// 发送HTTP GET请求resp, err := http.Get(url)if err != nil {c(nil, err)return}defer resp.Body.Close()// 读取响应数据data, err := ioutil.ReadAll(resp.Body)if err != nil {c(nil, err)return}time.Sleep(10*time.Second)// 调用回调函数,传递响应数据和错误信息c(data, nil)wg.Done()}()
}func main() {url := "https://www.baidu.com"wg.Add(1)fetch(url, func(data []byte, err error) {if err != nil {fmt.Println("Error:", err)return}fmt.Println(string(data))})fmt.Println("Waiting for response...")wg.Wait()
}
2.2 Sort.Slice函数
GO语言的Sort.Slice函数就使用了回调函数的思想,将比较大小的逻辑交给用户来实现,将代码的可定制化大大增强了,以下是一个使用的例子,将字符串按首字母大小进行排序:
package mainimport ("fmt""sort"
)func main() {strs := []string{"apple", "orange", "banana", "pear"}sort.Slice(strs, func(i, j int) bool {return strs[i] < strs[j]})fmt.Println(strs)
}
2.3 简易计算器
在此例子中,定义了一个type Operation func(int, int) int作为回调函数的类型,随后对此函数做了不同的实现,在主函数中,就可以直接传入函数名,即可完成不同的逻辑运算。
package mainimport "fmt"type Operation func(int, int) intfunc main() {// 加法运算result := calculate(10, 5, add)fmt.Println(result) // Output: 15// 减法运算result = calculate(10, 5, subtract)fmt.Println(result) // Output: 5// 乘法运算result = calculate(10, 5, multiply)fmt.Println(result) // Output: 50// 除法运算result = calculate(10, 5, divide)fmt.Println(result) // Output: 2
}// 计算函数,接受两个整数和一个运算函数作为参数,返回运算结果
func calculate(a, b int, op Operation) int {return op(a, b)
}// 加法函数,接受两个整数并返回它们的和
func add(a, b int) int {return a + b
}// 减法函数,接受两个整数并返回它们的差
func subtract(a, b int) int {return a - b
}// 乘法函数,接受两个整数并返回它们的积
func multiply(a, b int) int {return a * b
}// 除法函数,接受两个整数并返回它们的商
func divide(a, b int) int {return a / b
}
3.带参数和不带参数的回调函数的示例
1. 不带参数的回调函数
不带参数的回调函数是最简单的形式,通常用于简单的事件处理或状态通知。
package mainimport "fmt"// 定义一个不带参数的回调函数类型
type CallbackFunc func()// 定义一个函数,接受回调函数作为参数
func execute(callback CallbackFunc) {fmt.Println("Executing some logic...")callback() // 调用回调函数
}func main() {// 定义一个不带参数的回调函数myCallback := func() {fmt.Println("Callback executed!")}// 将回调函数传递给 execute 函数execute(myCallback)
}
运行结果
Executing some logic...
Callback executed!
2. 带参数的回调函数
带参数的回调函数可以传递数据给回调函数,使其更加灵活。参数的类型和数量可以根据需要定义。
package mainimport "fmt"// 定义一个带参数的回调函数类型
type CallbackFunc func(string, int) string// 定义一个函数,接受带参数的回调函数
func process(callback CallbackFunc, input string, number int) {fmt.Println("Processing input...")result := callback(input, number) // 调用回调函数fmt.Println("Result:", result)
}func main() {// 定义一个带参数的回调函数myCallback := func(input string, number int) string {return fmt.Sprintf("Callback received: %s, Number: %d", input, number)}// 将回调函数传递给 process 函数process(myCallback, "Hello, Go!", 42)
}
运行结果
Processing input...
Result: Callback received: Hello, Go!, Number: 42
3. 匿名函数作为回调
无论是带参数还是不带参数的回调函数,都可以使用匿名函数来实现。匿名函数可以在调用时直接定义,而不需要提前声明。
不带参数的匿名回调
package mainimport "fmt"// 定义一个不带参数的回调函数类型
type CallbackFunc func()// 定义一个函数,接受回调函数作为参数
func execute(callback CallbackFunc) {fmt.Println("Executing some logic...")callback() // 调用回调函数
}func main() {// 使用匿名函数作为回调execute(func() {fmt.Println("Anonymous callback executed!")})
}
运行结果
Executing some logic...
Anonymous callback executed!
带参数的匿名回调
package mainimport "fmt"// 定义一个带参数的回调函数类型
type CallbackFunc func(string, int) string// 定义一个函数,接受带参数的回调函数
func process(callback CallbackFunc, input string, number int) {fmt.Println("Processing input...")result := callback(input, number) // 调用回调函数fmt.Println("Result:", result)
}func main() {// 使用匿名函数作为带参数的回调process(func(input string, number int) string {return fmt.Sprintf("Anonymous callback received: %s, Number: %d", input, number)}, "Hello, Go!", 42)
}
运行结果
Processing input...
Result: Anonymous callback received: Hello, Go!, Number: 42
4. 回调函数的高级用法
异步操作中的回调
回调函数常用于异步操作,例如在完成某个耗时任务后通知调用者。
package mainimport ("fmt""time"
)// 定义一个带参数的回调函数类型
type CallbackFunc func(result string)// 模拟一个异步操作
func asyncOperation(callback CallbackFunc) {go func() {time.Sleep(2 * time.Second) // 模拟耗时操作callback("Operation completed!") // 调用回调函数}()
}func main() {fmt.Println("Starting async operation...")asyncOperation(func(result string) {fmt.Println("Callback:", result)})// 防止程序立即退出time.Sleep(3 * time.Second)
}
运行结果
Starting async operation...
Callback: Operation completed!
总结
- 不带参数的回调函数:适用于简单的事件处理或状态通知。
- 带参数的回调函数:可以传递数据给回调函数,更加灵活。
- 匿名函数:可以在调用时直接定义,适合简单的回调逻辑。
- 异步操作:回调函数常用于异步操作完成后的通知。