Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve hold docs #7500

Merged
merged 2 commits into from
Nov 18, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 32 additions & 17 deletions doc/how_to/performance/hold.md
Original file line number Diff line number Diff line change
@@ -1,39 +1,55 @@
# Batching updates with `hold`
# Batching Updates with `hold`

When working with interactive dashboards and applications in Panel, you might encounter situations where updating multiple components simultaneously causes unnecessary re-renders. This is because Panel generally dispatches any change to a parameter immediately. This can lead to performance issues and a less responsive user experience because each individual update may trigger re-renders on the frontend. The `hold` utility in Panel allows you to batch updates to the frontend, reducing the number of re-renders and improving performance.

In this guide, we'll explore how to use hold both as a context manager and as a decorator to optimize your Panel applications.
In this guide, we'll explore how to use `hold` both as a context manager and as a decorator to optimize your Panel applications.

## What is hold?
## What is `hold`?

The `hold` function is a context manager and decorator that temporarily holds events on a Bokeh Document. When you update multiple components within a hold block, the events are collected and dispatched all at once when the block exits. This means that the frontend will only re-render once, regardless of how many updates were made, leading to a smoother and more efficient user experience.
The `hold` function is a context manager and decorator that temporarily holds events on a Bokeh Document. When you update multiple components within a `hold` block, the events are collected and dispatched all at once when the block exits. This means that the frontend will only re-render once, regardless of how many updates were made, leading to a smoother and more efficient user experience.

Let’s try first **without `hold`** to understand the difference `hold` can make:

```{pyodide}
import panel as pn
from panel.io import hold

def increment(e):
for obj in column_0:
obj.object = str(e.new)

column_0 = pn.FlexBox(*['0']*100)
button = pn.widgets.Button(name='Increment', on_click=increment)

pn.Column(column_0, button).servable()
```

## Using `hold`

### As a decorator
### As a Decorator

If you have a function that updates components and you want to ensure that all updates are held, you can use hold as a decorator. E.g. here we update 100 components at once. If you do not hold then each of these events is sent and applied in series, potentially resulting in visible updates.
If you have a function that updates components and you want to ensure that all updates are held, you can use `hold` as a decorator. For example, here we update 100 components at once. If you do not use `hold`, each of these events is sent and applied in series, potentially resulting in visible updates.

```{pyodide}
import panel as pn
from panel.io import hold

@hold()
def increment(e):
for obj in column:
for obj in column_1:
obj.object = str(e.new)

column = pn.FlexBox(*['0']*100)
column_1 = pn.FlexBox(*['0']*100)
button = pn.widgets.Button(name='Increment', on_click=increment)

pn.Column(column, button).servable()
pn.Column(column_1, button).servable()
```

Applying the hold decorator means all the updates are sent in a single Websocket message and applied on the frontend simultaneously.
Applying the `hold` decorator means all the updates are sent in a single WebSocket message and applied on the frontend simultaneously.

### As a context manager
### As a Context Manager

Alternatively, the `hold` function can be used as a context manager, potentially giving you finer grained control over which events are batched and which are not:
Alternatively, the `hold` function can be used as a context manager, potentially giving you finer-grained control over which events are batched and which are not:

```{pyodide}
import time
Expand All @@ -43,15 +59,14 @@ from panel.io import hold

def increment(e):
with button.param.update(name='Incrementing...', disabled=True):
time.sleep(0.5)
with hold():
for obj in column:
for obj in column_2:
obj.object = str(e.new)

column = pn.FlexBox(*['0']*100)
column_2 = pn.FlexBox(*['0']*100)
button = pn.widgets.Button(name='Increment', on_click=increment)

pn.Column(column, button).servable()
pn.Column(column_2, button).servable()
```

Here the updates to the `Button` are dispatched immediately while the updates to the counters are batched.
Here the updates to the `Button` are dispatched immediately, while the updates to the counters are batched.
Loading