Vuejs has a interesting and great way to avoid such feedback loops even in the presence of 2-way data binding. Most importantly you wouldn't use any watchers/effects to trigger logic on property-updates, but rather you use what they call computed property, which can do conversion on the fly, in both directions, giving different behavior on read and write.
celcius: {
get() { return this._inputCelcius || ctof(this._inputFahrenheit) }
set(newVal) {
this._inputCelcius = newVal
this._inputFahrenheit = null
}
}
Repeat for fahrenheit. Now i've seen shorter examples but most of them are very coupled to the UI, with each event on textbox writing directly to the other textbox attributes, this property can be bound to any number of textboxes and sliders without any additional code to keep them in sync. Similar techniques can be used for more complex synchronizations with conversions, like a date picker that allows both text-input and clicking a calendar.