Async API

To interact with the Excel object model, scripts and notebooks let you use the same syntax as the classic, locally installed xlwings (sync API). This often works fine, but because xlwings Lite runs in a browser, it talks to Excel differently. That difference creates three limitations, and the async API gives you a method to work around each one:

  1. All cell values are loaded up front. Getting a sync book (xw.books.active for notebooks or using the xw.Book type hint in scripts) fetches all values from the entire workbook, which can be slow and may cause the browser to run out of memory for large workbooks.

    await myrange.get_value() with an async Book reads only the values you ask for.

  2. Writes are queued. When you assign a value to a range, for example, it’s only applied at the end of a notebook cell or when a script finishes, not immediately.

    await book.flush() applies queued writes immediately.

  3. Reads can be outdated. Anything you read via the sync API reflects the workbook state from the start of the notebook cell or the start of the script.

    await book.load() refreshes everything.

The following sections cover each method in detail.

get_value(): reading values on demand

To read values on demand:

  • Use an async Book object, which disables the loading of all values up front:

    • Notebook: await xw.books.get_active()

    • Script: xw.BookAsync (type hint)

  • Use await myrange.get_value() instead of myrange.value to load the values live.

In the following example, only the value of a single cell is loaded, no matter how big the workbook is:

# %%
import xlwings as xw

book = await xw.books.get_active()  # sync version: book = xw.books.active
myrange = book.sheets[0]["A1"]
await myrange.get_value()  # sync version: myrange.value
import xlwings as xw
from xlwings import script

@script
async def my_script(book: xw.BookAsync):  # sync version: def my_script(book: xw.Book)
    myrange = book.sheets[0]["A1"]
    await myrange.get_value()  # sync version: myrange.value

Note

  • Notebooks let you await at the top level without requiring an async def.

  • Scripts must use await inside of an async def.

get_value() respects the same converters and options as value, so you can, for example, read a range directly into a pandas DataFrame:

import pandas as pd

df = await (
    book.sheets["Sheet1"]["A1:C10"]
    .options(pd.DataFrame, index=False)
    .get_value()
)

While await get_value() technically also works with a sync book to get the current values, a sync book still loads the whole workbook up front.

flush(): writing immediately

When writing to a workbook, xlwings Lite queues every action and applies them in order at the end of a notebook cell or when the script finishes. If you need to flush queued writes to Excel before the automatic flush happens, call await book.flush():

# %%
book = await xw.books.get_active()
book.sheets[0]["A1"].value = "Hello xlwings!"
await book.flush()
await book.sheets[0]["A1"].get_value()

# Output
# 'Hello xlwings!'
import xlwings as xw
from xlwings import script

@script
async def my_script(book: xw.BookAsync):
    book.sheets[0]["A1"].value = "Hello xlwings!"
    await book.flush()
    print(await book.sheets[0]["A1"].get_value())
    # prints 'Hello xlwings!'

Here are some common scenarios where you need to use await book.flush():

  • After writing a value to an Excel cell, call await book.flush() before reading that value—or a dependent cell—within the same notebook cell or script (example above).

  • If you want to see the printed output immediately in the Output pane, use await book.flush() after print().

  • If you call a method that writes to the file system, such as mysheet["A1:D10"].to_png("/data/range.png"), call await book.flush() before accessing the file in the same notebook cell or script.

load(): reading the current workbook state

Before running a script or a notebook cell, xlwings Lite automatically runs await book.load(), so you rarely need to call it explicitly. To refresh the workbook state midway through a notebook cell or script, use await book.load() or await mysheet.load():

# %%
book = await xw.books.get_active()
sheet1 = book.sheets[0]
await book.load()  # or: await sheet1.load()
sheet1.tables
import xlwings as xw
from xlwings import script

@script
async def my_script(book: xw.BookAsync):
    sheet1 = book.sheets[0]
    await book.load()  # or: await sheet1.load()
    sheet1.tables

A few things to keep in mind:

  • On an async book, await book.load() fetches everything except values since you should access values via await myrange.get_value(). On a sync book, await book.load() includes values. If you want to exclude values on a sync book, use await book.load(values=False).

  • Calling load() on a sheet object rather than the book loads only that sheet instead of the whole book, so it’s slightly more efficient.