Handle slow callback functions when using x_range.on_change in Bokeh server
<p>I'm trying to use callback functions to generate new data in a Bokeh server plot when the axis range changes. This works fine in principle: I've attached the necessary function to the plot using <code>p.x_range.on_change('end', my_callback_function)</code>, <code>my_callback_function</code> is executed and updates the plot.</p>

<p>The problem I'm facing is that the callback function can take a long time to complete. If the user pans the plot or zooms several steps while <code>my_callback_function</code> is still running, more <code>on_change</code> events are fired and new calls to <code>my_callback_function</code> pile up. This leads to the plot updating for a long time after the user has stopped panning or zooming.</p>

<p>Is there a way to cancel the execution of a callback function in case a new call is executed? Or is it possible to specify that only one call to a callback function may be queued up at any one time?</p>

<p>I tried deactivating the callback while the function is running by setting <code>p.x_range.remove_on_change('end', my_callback_function)</code> at the start of <code>my_callback_function</code>, but apparently that deactivation doesn't happen until the call to <code>my_callback_function</code> is complete: new calls are still piling up.</p>

<hr>

<p>Here's a small example to play with: save it as <code>test.py</code> and execute it using <code>bokeh serve test.py --show</code></p>

<pre><code>import bokeh.plotting
import numpy
import time

def generate_data(start, end):
    time.sleep(1)
    newx = numpy.linspace(start + 0.1 * (end - start), end - 0.1 * (end - start), 10)
    newy = numpy.random.rand(10)
    return newx, newy

def my_callback_function(param, old, new):
    # I tried deactivating the callback once it's running...
    # p.x_range.remove_on_change('end', my_callback_function)
    x, y = generate_data(p.x_range.start, p.x_range.end)
    ds.data = {
        'x': x,
        'y': y
    }
    # ... and activating it again when done, but that doesn't work, the function calls still pile up
    # p.x_range.on_change('end', my_callback_function)

x, y = generate_data(0, 10)

p = bokeh.plotting.figure(x_range=[0, 10])
l = p.line(x, y)
ds = l.data_source

p.x_range.on_change('end', my_callback_function)

bokeh.plotting.curdoc().add_root(p)
</code></pre>