Callback pattern for handling socket response
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
4
down vote
favorite
I am in the process of learning go and am coming from a PHP, JS, and Nodejs background.
I created a package that is a client which connects to a socket server and processes the data received from the server. I am using a callback pattern and am wondering if this is frowned upon in the Go community. Here are the relevant code snippets:
// SocketClient allows handles the socket connection to a server
type SocketClient struct
Host string
Path string
conn *websocket.Conn
// SocketResponse function callback for data from socket
type SocketResponse func(res byte, err error)
// Connect create the connection with the host
func (sc SocketClient) Connect(cb SocketResponse)
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt)
u := url.URLScheme: "wss", Host: sc.Host, Path: sc.Path
var err error
sc.conn, _, err = websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil
cb(nil, err)
defer sc.conn.Close()
done := make(chan struct)
// anonymous function call
go func()
defer sc.conn.Close()
defer close(done)
for
_, message, err := sc.conn.ReadMessage()
if err != nil
cb(nil, err)
cb(message, nil)
()
// ...
And then in another file I actually initialize SocketClient and call Connect...
sc.Connect(func(res byte, err error)
// handle the response
)
go socket callback
add a comment |Â
up vote
4
down vote
favorite
I am in the process of learning go and am coming from a PHP, JS, and Nodejs background.
I created a package that is a client which connects to a socket server and processes the data received from the server. I am using a callback pattern and am wondering if this is frowned upon in the Go community. Here are the relevant code snippets:
// SocketClient allows handles the socket connection to a server
type SocketClient struct
Host string
Path string
conn *websocket.Conn
// SocketResponse function callback for data from socket
type SocketResponse func(res byte, err error)
// Connect create the connection with the host
func (sc SocketClient) Connect(cb SocketResponse)
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt)
u := url.URLScheme: "wss", Host: sc.Host, Path: sc.Path
var err error
sc.conn, _, err = websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil
cb(nil, err)
defer sc.conn.Close()
done := make(chan struct)
// anonymous function call
go func()
defer sc.conn.Close()
defer close(done)
for
_, message, err := sc.conn.ReadMessage()
if err != nil
cb(nil, err)
cb(message, nil)
()
// ...
And then in another file I actually initialize SocketClient and call Connect...
sc.Connect(func(res byte, err error)
// handle the response
)
go socket callback
add a comment |Â
up vote
4
down vote
favorite
up vote
4
down vote
favorite
I am in the process of learning go and am coming from a PHP, JS, and Nodejs background.
I created a package that is a client which connects to a socket server and processes the data received from the server. I am using a callback pattern and am wondering if this is frowned upon in the Go community. Here are the relevant code snippets:
// SocketClient allows handles the socket connection to a server
type SocketClient struct
Host string
Path string
conn *websocket.Conn
// SocketResponse function callback for data from socket
type SocketResponse func(res byte, err error)
// Connect create the connection with the host
func (sc SocketClient) Connect(cb SocketResponse)
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt)
u := url.URLScheme: "wss", Host: sc.Host, Path: sc.Path
var err error
sc.conn, _, err = websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil
cb(nil, err)
defer sc.conn.Close()
done := make(chan struct)
// anonymous function call
go func()
defer sc.conn.Close()
defer close(done)
for
_, message, err := sc.conn.ReadMessage()
if err != nil
cb(nil, err)
cb(message, nil)
()
// ...
And then in another file I actually initialize SocketClient and call Connect...
sc.Connect(func(res byte, err error)
// handle the response
)
go socket callback
I am in the process of learning go and am coming from a PHP, JS, and Nodejs background.
I created a package that is a client which connects to a socket server and processes the data received from the server. I am using a callback pattern and am wondering if this is frowned upon in the Go community. Here are the relevant code snippets:
// SocketClient allows handles the socket connection to a server
type SocketClient struct
Host string
Path string
conn *websocket.Conn
// SocketResponse function callback for data from socket
type SocketResponse func(res byte, err error)
// Connect create the connection with the host
func (sc SocketClient) Connect(cb SocketResponse)
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt)
u := url.URLScheme: "wss", Host: sc.Host, Path: sc.Path
var err error
sc.conn, _, err = websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil
cb(nil, err)
defer sc.conn.Close()
done := make(chan struct)
// anonymous function call
go func()
defer sc.conn.Close()
defer close(done)
for
_, message, err := sc.conn.ReadMessage()
if err != nil
cb(nil, err)
cb(message, nil)
()
// ...
And then in another file I actually initialize SocketClient and call Connect...
sc.Connect(func(res byte, err error)
// handle the response
)
go socket callback
edited Jan 27 at 22:49
200_success
123k14143401
123k14143401
asked Jan 27 at 22:17
kyle
1236
1236
add a comment |Â
add a comment |Â
1 Answer
1
active
oldest
votes
up vote
3
down vote
accepted
I am using a callback pattern and am wondering if this is frowned upon in the Go community.
"callback pattern" shouldn't be frowned upon (they are used in "first-class functions" for instance: https://blog.golang.org/first-class-functions-in-go-and-new-go https://dave.cheney.net/2016/11/13/do-not-fear-first-class-functions)
However regarding your code, I don't think that it looks like idiomatic go code.
Don't just check errors, handle them gracefully
sc.conn, _, err = websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil
cb(nil, err)
You pass the err
to your callback, but you then continue inside the Connect
function: if you couldn't dial, why do you continue?
Never start a goroutine without knowing how it will stop
go func()
defer sc.conn.Close()
defer close(done)
for
_, message, err := sc.conn.ReadMessage()
if err != nil
cb(nil, err)
cb(message, nil)
()
The inner for
loop will never stop (so will your goroutine).
One possibility would be to change it like this:
// SocketResponse function callback for data from socket
type SocketResponse func(res byte)
// Connect create the connection with the host
func (sc SocketClient) Connect(cb SocketResponse) error
u := url.URLScheme: "wss", Host: sc.Host, Path: sc.Path
var err error
sc.conn, _, err = websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil
return err
defer sc.conn.Close()
for
_, message, err := sc.conn.ReadMessage()
if err != nil
return err
cb(message)
Some properties of this code :
- on dial error, it stops
- it reads all the messages until one error is encountered
- it blocks (but
go sc.Connect()
is easy to write)
Some possible evolutions:
- to continue even in case of errors, you could return a channel of errors (and send all errors encountered)
- also use a channel to report the received messages
- to control when the
Connect
method should stop, you could use acontext
or split your method in 3 parts:Connect
,ReadMessage
&Close
Thank you I really appreciate the information about the callback as well as the detailed review in general! Are you recommending to use a channel rather than a callback? If I were to send the error in the callback and then return to stop the function could I then just implement a reconnect function that has a backoff procedure and have the code that started the listening to call reconnect? Also don't I need to block on connect before listening?
â kyle
Feb 2 at 16:03
Are you recommending to use a channel rather than a callback?
both options are valid and kind of interchangeable (the provided callback can send the values into a channel / the goroutine receiving the values may call a callback). The drawback of the channel is the buffer size management (either the channel is given as argument, or the size of it), but its closing mechanism is a nice way to indicate the end of the messages (whereas a callback would need some additional logic).
â oliverpool
Feb 4 at 20:27
If I were to send the error...
it actually depends on the error: if it is an error meaning that no moreRead
may succeed (connexion closed for instance) in this case a new connection is needed. But if it is an error just for decoding this message (formatting or transmission error), subsequentRead
should succeed. What can be done when (connect, listen, reconnect...) depends mainly on the library!
â oliverpool
Feb 4 at 20:30
add a comment |Â
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
3
down vote
accepted
I am using a callback pattern and am wondering if this is frowned upon in the Go community.
"callback pattern" shouldn't be frowned upon (they are used in "first-class functions" for instance: https://blog.golang.org/first-class-functions-in-go-and-new-go https://dave.cheney.net/2016/11/13/do-not-fear-first-class-functions)
However regarding your code, I don't think that it looks like idiomatic go code.
Don't just check errors, handle them gracefully
sc.conn, _, err = websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil
cb(nil, err)
You pass the err
to your callback, but you then continue inside the Connect
function: if you couldn't dial, why do you continue?
Never start a goroutine without knowing how it will stop
go func()
defer sc.conn.Close()
defer close(done)
for
_, message, err := sc.conn.ReadMessage()
if err != nil
cb(nil, err)
cb(message, nil)
()
The inner for
loop will never stop (so will your goroutine).
One possibility would be to change it like this:
// SocketResponse function callback for data from socket
type SocketResponse func(res byte)
// Connect create the connection with the host
func (sc SocketClient) Connect(cb SocketResponse) error
u := url.URLScheme: "wss", Host: sc.Host, Path: sc.Path
var err error
sc.conn, _, err = websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil
return err
defer sc.conn.Close()
for
_, message, err := sc.conn.ReadMessage()
if err != nil
return err
cb(message)
Some properties of this code :
- on dial error, it stops
- it reads all the messages until one error is encountered
- it blocks (but
go sc.Connect()
is easy to write)
Some possible evolutions:
- to continue even in case of errors, you could return a channel of errors (and send all errors encountered)
- also use a channel to report the received messages
- to control when the
Connect
method should stop, you could use acontext
or split your method in 3 parts:Connect
,ReadMessage
&Close
Thank you I really appreciate the information about the callback as well as the detailed review in general! Are you recommending to use a channel rather than a callback? If I were to send the error in the callback and then return to stop the function could I then just implement a reconnect function that has a backoff procedure and have the code that started the listening to call reconnect? Also don't I need to block on connect before listening?
â kyle
Feb 2 at 16:03
Are you recommending to use a channel rather than a callback?
both options are valid and kind of interchangeable (the provided callback can send the values into a channel / the goroutine receiving the values may call a callback). The drawback of the channel is the buffer size management (either the channel is given as argument, or the size of it), but its closing mechanism is a nice way to indicate the end of the messages (whereas a callback would need some additional logic).
â oliverpool
Feb 4 at 20:27
If I were to send the error...
it actually depends on the error: if it is an error meaning that no moreRead
may succeed (connexion closed for instance) in this case a new connection is needed. But if it is an error just for decoding this message (formatting or transmission error), subsequentRead
should succeed. What can be done when (connect, listen, reconnect...) depends mainly on the library!
â oliverpool
Feb 4 at 20:30
add a comment |Â
up vote
3
down vote
accepted
I am using a callback pattern and am wondering if this is frowned upon in the Go community.
"callback pattern" shouldn't be frowned upon (they are used in "first-class functions" for instance: https://blog.golang.org/first-class-functions-in-go-and-new-go https://dave.cheney.net/2016/11/13/do-not-fear-first-class-functions)
However regarding your code, I don't think that it looks like idiomatic go code.
Don't just check errors, handle them gracefully
sc.conn, _, err = websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil
cb(nil, err)
You pass the err
to your callback, but you then continue inside the Connect
function: if you couldn't dial, why do you continue?
Never start a goroutine without knowing how it will stop
go func()
defer sc.conn.Close()
defer close(done)
for
_, message, err := sc.conn.ReadMessage()
if err != nil
cb(nil, err)
cb(message, nil)
()
The inner for
loop will never stop (so will your goroutine).
One possibility would be to change it like this:
// SocketResponse function callback for data from socket
type SocketResponse func(res byte)
// Connect create the connection with the host
func (sc SocketClient) Connect(cb SocketResponse) error
u := url.URLScheme: "wss", Host: sc.Host, Path: sc.Path
var err error
sc.conn, _, err = websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil
return err
defer sc.conn.Close()
for
_, message, err := sc.conn.ReadMessage()
if err != nil
return err
cb(message)
Some properties of this code :
- on dial error, it stops
- it reads all the messages until one error is encountered
- it blocks (but
go sc.Connect()
is easy to write)
Some possible evolutions:
- to continue even in case of errors, you could return a channel of errors (and send all errors encountered)
- also use a channel to report the received messages
- to control when the
Connect
method should stop, you could use acontext
or split your method in 3 parts:Connect
,ReadMessage
&Close
Thank you I really appreciate the information about the callback as well as the detailed review in general! Are you recommending to use a channel rather than a callback? If I were to send the error in the callback and then return to stop the function could I then just implement a reconnect function that has a backoff procedure and have the code that started the listening to call reconnect? Also don't I need to block on connect before listening?
â kyle
Feb 2 at 16:03
Are you recommending to use a channel rather than a callback?
both options are valid and kind of interchangeable (the provided callback can send the values into a channel / the goroutine receiving the values may call a callback). The drawback of the channel is the buffer size management (either the channel is given as argument, or the size of it), but its closing mechanism is a nice way to indicate the end of the messages (whereas a callback would need some additional logic).
â oliverpool
Feb 4 at 20:27
If I were to send the error...
it actually depends on the error: if it is an error meaning that no moreRead
may succeed (connexion closed for instance) in this case a new connection is needed. But if it is an error just for decoding this message (formatting or transmission error), subsequentRead
should succeed. What can be done when (connect, listen, reconnect...) depends mainly on the library!
â oliverpool
Feb 4 at 20:30
add a comment |Â
up vote
3
down vote
accepted
up vote
3
down vote
accepted
I am using a callback pattern and am wondering if this is frowned upon in the Go community.
"callback pattern" shouldn't be frowned upon (they are used in "first-class functions" for instance: https://blog.golang.org/first-class-functions-in-go-and-new-go https://dave.cheney.net/2016/11/13/do-not-fear-first-class-functions)
However regarding your code, I don't think that it looks like idiomatic go code.
Don't just check errors, handle them gracefully
sc.conn, _, err = websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil
cb(nil, err)
You pass the err
to your callback, but you then continue inside the Connect
function: if you couldn't dial, why do you continue?
Never start a goroutine without knowing how it will stop
go func()
defer sc.conn.Close()
defer close(done)
for
_, message, err := sc.conn.ReadMessage()
if err != nil
cb(nil, err)
cb(message, nil)
()
The inner for
loop will never stop (so will your goroutine).
One possibility would be to change it like this:
// SocketResponse function callback for data from socket
type SocketResponse func(res byte)
// Connect create the connection with the host
func (sc SocketClient) Connect(cb SocketResponse) error
u := url.URLScheme: "wss", Host: sc.Host, Path: sc.Path
var err error
sc.conn, _, err = websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil
return err
defer sc.conn.Close()
for
_, message, err := sc.conn.ReadMessage()
if err != nil
return err
cb(message)
Some properties of this code :
- on dial error, it stops
- it reads all the messages until one error is encountered
- it blocks (but
go sc.Connect()
is easy to write)
Some possible evolutions:
- to continue even in case of errors, you could return a channel of errors (and send all errors encountered)
- also use a channel to report the received messages
- to control when the
Connect
method should stop, you could use acontext
or split your method in 3 parts:Connect
,ReadMessage
&Close
I am using a callback pattern and am wondering if this is frowned upon in the Go community.
"callback pattern" shouldn't be frowned upon (they are used in "first-class functions" for instance: https://blog.golang.org/first-class-functions-in-go-and-new-go https://dave.cheney.net/2016/11/13/do-not-fear-first-class-functions)
However regarding your code, I don't think that it looks like idiomatic go code.
Don't just check errors, handle them gracefully
sc.conn, _, err = websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil
cb(nil, err)
You pass the err
to your callback, but you then continue inside the Connect
function: if you couldn't dial, why do you continue?
Never start a goroutine without knowing how it will stop
go func()
defer sc.conn.Close()
defer close(done)
for
_, message, err := sc.conn.ReadMessage()
if err != nil
cb(nil, err)
cb(message, nil)
()
The inner for
loop will never stop (so will your goroutine).
One possibility would be to change it like this:
// SocketResponse function callback for data from socket
type SocketResponse func(res byte)
// Connect create the connection with the host
func (sc SocketClient) Connect(cb SocketResponse) error
u := url.URLScheme: "wss", Host: sc.Host, Path: sc.Path
var err error
sc.conn, _, err = websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil
return err
defer sc.conn.Close()
for
_, message, err := sc.conn.ReadMessage()
if err != nil
return err
cb(message)
Some properties of this code :
- on dial error, it stops
- it reads all the messages until one error is encountered
- it blocks (but
go sc.Connect()
is easy to write)
Some possible evolutions:
- to continue even in case of errors, you could return a channel of errors (and send all errors encountered)
- also use a channel to report the received messages
- to control when the
Connect
method should stop, you could use acontext
or split your method in 3 parts:Connect
,ReadMessage
&Close
answered Feb 2 at 9:15
oliverpool
1,542425
1,542425
Thank you I really appreciate the information about the callback as well as the detailed review in general! Are you recommending to use a channel rather than a callback? If I were to send the error in the callback and then return to stop the function could I then just implement a reconnect function that has a backoff procedure and have the code that started the listening to call reconnect? Also don't I need to block on connect before listening?
â kyle
Feb 2 at 16:03
Are you recommending to use a channel rather than a callback?
both options are valid and kind of interchangeable (the provided callback can send the values into a channel / the goroutine receiving the values may call a callback). The drawback of the channel is the buffer size management (either the channel is given as argument, or the size of it), but its closing mechanism is a nice way to indicate the end of the messages (whereas a callback would need some additional logic).
â oliverpool
Feb 4 at 20:27
If I were to send the error...
it actually depends on the error: if it is an error meaning that no moreRead
may succeed (connexion closed for instance) in this case a new connection is needed. But if it is an error just for decoding this message (formatting or transmission error), subsequentRead
should succeed. What can be done when (connect, listen, reconnect...) depends mainly on the library!
â oliverpool
Feb 4 at 20:30
add a comment |Â
Thank you I really appreciate the information about the callback as well as the detailed review in general! Are you recommending to use a channel rather than a callback? If I were to send the error in the callback and then return to stop the function could I then just implement a reconnect function that has a backoff procedure and have the code that started the listening to call reconnect? Also don't I need to block on connect before listening?
â kyle
Feb 2 at 16:03
Are you recommending to use a channel rather than a callback?
both options are valid and kind of interchangeable (the provided callback can send the values into a channel / the goroutine receiving the values may call a callback). The drawback of the channel is the buffer size management (either the channel is given as argument, or the size of it), but its closing mechanism is a nice way to indicate the end of the messages (whereas a callback would need some additional logic).
â oliverpool
Feb 4 at 20:27
If I were to send the error...
it actually depends on the error: if it is an error meaning that no moreRead
may succeed (connexion closed for instance) in this case a new connection is needed. But if it is an error just for decoding this message (formatting or transmission error), subsequentRead
should succeed. What can be done when (connect, listen, reconnect...) depends mainly on the library!
â oliverpool
Feb 4 at 20:30
Thank you I really appreciate the information about the callback as well as the detailed review in general! Are you recommending to use a channel rather than a callback? If I were to send the error in the callback and then return to stop the function could I then just implement a reconnect function that has a backoff procedure and have the code that started the listening to call reconnect? Also don't I need to block on connect before listening?
â kyle
Feb 2 at 16:03
Thank you I really appreciate the information about the callback as well as the detailed review in general! Are you recommending to use a channel rather than a callback? If I were to send the error in the callback and then return to stop the function could I then just implement a reconnect function that has a backoff procedure and have the code that started the listening to call reconnect? Also don't I need to block on connect before listening?
â kyle
Feb 2 at 16:03
Are you recommending to use a channel rather than a callback?
both options are valid and kind of interchangeable (the provided callback can send the values into a channel / the goroutine receiving the values may call a callback). The drawback of the channel is the buffer size management (either the channel is given as argument, or the size of it), but its closing mechanism is a nice way to indicate the end of the messages (whereas a callback would need some additional logic).â oliverpool
Feb 4 at 20:27
Are you recommending to use a channel rather than a callback?
both options are valid and kind of interchangeable (the provided callback can send the values into a channel / the goroutine receiving the values may call a callback). The drawback of the channel is the buffer size management (either the channel is given as argument, or the size of it), but its closing mechanism is a nice way to indicate the end of the messages (whereas a callback would need some additional logic).â oliverpool
Feb 4 at 20:27
If I were to send the error...
it actually depends on the error: if it is an error meaning that no more Read
may succeed (connexion closed for instance) in this case a new connection is needed. But if it is an error just for decoding this message (formatting or transmission error), subsequent Read
should succeed. What can be done when (connect, listen, reconnect...) depends mainly on the library!â oliverpool
Feb 4 at 20:30
If I were to send the error...
it actually depends on the error: if it is an error meaning that no more Read
may succeed (connexion closed for instance) in this case a new connection is needed. But if it is an error just for decoding this message (formatting or transmission error), subsequent Read
should succeed. What can be done when (connect, listen, reconnect...) depends mainly on the library!â oliverpool
Feb 4 at 20:30
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f186158%2fcallback-pattern-for-handling-socket-response%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password