Can i load data asynchronously in Panel?

Hi @prithivi, this is indeed possible but Processes are a bit more complicated than threads because they do not share memory and therefore your code and data needs to be serialized (by pickle) in order for it to work. The example above can be modified to make this work but you need to

  1. define your intensive computation function (blocking_function) outside of the TestClass otherwise cannot be pickled. I’m not sure why this happens exactly but I think its because its a subclass of param.Parameterized.
  2. Add the if __name__ == '__main__': check (at least on windows). This ensures the panel is only launched once for each process.

This example below should do what you want:

import param
import panel as pn
from concurrent.futures import ProcessPoolExecutor
from tornado.ioloop import IOLoop
import time
import numpy as np

executor = ProcessPoolExecutor(max_workers=2)


def blocking_function():
    time.sleep(np.random.randint(1, 5))
    return 5


class TestClass(param.Parameterized):
    do_stuff = param.Action(lambda self: self._do_calc())
    select = param.Selector(objects=range(10))
    result = param.Number(0)
    slider = param.Number(2, bounds=(0, 10))
    text = param.String()

    def __init__(self, **params):
        super(TestClass, self).__init__(**params)
        self.progress_widget = pn.widgets.Progress(active=True, value=0, align="center", sizing_mode="stretch_width")
        self.completed_tasks = 0
        self.num_tasks = 10

        self.widgets = pn.Param(self.param)
        self.col = pn.Column(*self.widgets)
        self.col.append(self.progress_widget)

    @param.depends('slider', watch=True)
    def _on_slider_change(self):
        # This functions does some other python code which we want to keep responsive
        self.text = str(self.slider)

    def _do_calc(self):
        loop = IOLoop.current()
        for i in range(self.num_tasks):
            future = executor.submit(blocking_function)
            loop.add_future(future, self.update)

    def update(self, future):
        number = future.result()
        with pn.io.unlocked():
            self.result += number
            self.completed_tasks += 1
            self.progress_widget.value = int(100 * self.completed_tasks / self.num_tasks)

    def blocking_task(self):
        # This function is not pickleable: Can't pickle local object 'Param.widget.<locals>.link'
        time.sleep(np.random.randint(1, 5))
        return 5

    def panel(self):
        return self.col


if __name__ == '__main__':
    tc = TestClass()
    pn.serve(tc.panel())
1 Like