vendredi 19 juillet 2019

Synchronous API wrapper over asynchronous callback-based API

I'm using the pion/webrtc Go library in my project and found this problem that the callback-based API the library provides (which mirrors the JavaScript API of WebRTC) can be awkward to use in Go.

For example, doing the following

conn.OnTrack(func(...) { ... })
conn.OnICEConnectionStateChange(func(...) { ... })

is typical in JavaScript, but in Go, this has a few problems:

  • This API makes it easy to introduce data race, if the callbacks are called in parallel.
  • The callback-based API propagates to other part of the codebase and makes everything takes callbacks.

What's the conventional way to handle this situation in Go? I'm new to Go and I read that synchronous API is preferred in Go because Goroutines are cheap. So perhaps one possible design is to use a channel to synchronize the callbacks:

msgChan := make(chan Msg)
// or use a separate channel for each type of event?

conn.OnTrack(func(...) {
  msgChan <- onTrackMsg
})
conn.OnICEConnectionStateChange(func(...) {
  msgChan <- onStateChangeMsg
})

for {
  msg := <-msgChan
  // do something depending on the type of msg
}

I think forcing synchronization with channels basically mimics the single-threaded nature of JavaScript.

Anyway, how do people usually model event-driven workflow in Go?

Aucun commentaire:

Enregistrer un commentaire