Go 感受回调函数的魅力
Go 感受回调函数的魅力
在用mqtt的SDK时候,感觉回调一脸蒙蔽,在自己实现之后,还是有点意思的
官方sdk原始代码
/*
* Copyright (c) 2013 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Seth Hoenig
* Allan Stockdill-Mander
* Mike Robertson
*/
package main
import (
"fmt"
"log"
"os"
"time"
"github.com/eclipse/paho.mqtt.golang"
)
var f mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) {
fmt.Printf("TOPIC: %s\n", msg.Topic())
fmt.Printf("MSG: %s\n", msg.Payload())
}
func main() {
mqtt.DEBUG = log.New(os.Stdout, "", 0)
mqtt.ERROR = log.New(os.Stdout, "", 0)
opts := mqtt.NewClientOptions().AddBroker("tcp://iot.eclipse.org:1883").SetClientID("gotrivial")
opts.SetKeepAlive(2 * time.Second)
opts.SetDefaultPublishHandler(f)
opts.SetPingTimeout(1 * time.Second)
c := mqtt.NewClient(opts)
if token := c.Connect(); token.Wait() && token.Error() != nil {
panic(token.Error())
}
// 注意下面的f
if token := c.Subscribe("go-mqtt/sample", 0, f); token.Wait() && token.Error() != nil {
fmt.Println(token.Error())
os.Exit(1)
}
for i := 0; i < 5; i++ {
text := fmt.Sprintf("this is msg #%d!", i)
token := c.Publish("go-mqtt/sample", 0, false, text)
token.Wait()
}
time.Sleep(6 * time.Second)
if token := c.Unsubscribe("go-mqtt/sample"); token.Wait() && token.Error() != nil {
fmt.Println(token.Error())
os.Exit(1)
}
c.Disconnect(250)
time.Sleep(1 * time.Second)
}
我自己还原的
mq.go
package mq
type Sub func(c *Client, mgs []byte)
type Client struct {
SubData chan []byte
}
func NewClient() *Client {
return &Client{SubData:make(chan []byte, 1)}
}
func (c *Client)Subscribe(topic string, qos int, fn Sub) {
go func() {
for {
select {
case data := <- c.SubData:
// 可以在这里面进行进一步处理
fn(c, data)
}
}
}()
}
main.go
package main
import (
"aaa/mq"
"encoding/json"
"fmt"
"log"
"time"
)
const _defaultTopic = "+/a"
type Data struct {
Topic string
PayLoad []byte
}
var callback mq.Sub = func(client *mq.Client, msg []byte) {
var d Data
err := json.Unmarshal(msg,&d)
if err != nil {
log.Println(err)
}
fmt.Println("获取的topic", d.Topic)
fmt.Println("获取的数据", string(d.PayLoad))
}
func main() {
client := mq.NewClient()
client.Subscribe(_defaultTopic, 1, callback)
// 模拟pub数据
for {
d := Data{
Topic: _defaultTopic,
PayLoad: []byte(time.Now().String()),
}
marshal, err := json.Marshal(d)
if err != nil {
log.Println(err)
}
client.SubData<- marshal
time.Sleep(time.Millisecond * 500)
}
}
理解
回调函数在结构上出来,传进去的参数让用户自己处理
有意思的回调理解
打个比方,有一家旅馆提供叫醒服务,但是要求旅客自己决定叫醒的方法。可以是打客房电话,也可以是派服务员去敲门,睡得死怕耽误事的,还可以要求往自己头上浇盆水。这里,“叫醒”这个行为是旅馆提供的,相当于库函数,但是叫醒的方式是由旅客决定并告诉旅馆的,也就是回调函数。而旅客告诉旅馆怎么叫醒自己的动作,也就是把回调函数传入库函数的动作,称为登记回调函数