Search code examples
javascriptexceloffice-jsweb-worker

Using webworkers in typescript Excel add-in


I have an add-in (built with officejs) which downloads, processes and puts large data arrays to Excel. The problem is when the processing takes too long Excel kills the add-in immediately. I tried to delegate all the work to a webworker in order to make the UI perform only the work of a messenger. The problem is that I can't call Office.initialize in a webworker (I understand that a webworker has its own scope, etc.). My question is - is it possible to make calls to Excel from the webworker and if so - how to make them correctly?

Edit: Here's example source:

app.js

function createTableWorker() {
    if (typeof(worker) == "undefined"){
        console.log("creating new worker!");
        worker = new Worker("test_worker.js");
    }

    worker.onmessage = function(event) {
        console.log("From worker: " + event.data);
    };

    worker.postMessage("message");
}

worker.js

this.onmessage = function(e) {
    console.log("Worker On Message!");
    importScripts("https://appsforoffice.microsoft.com/lib/beta/hosted/office.debug.js");
    doExcelWork();
}

function doExcelWork() {
    Excel.run(function (context) {
        debugger;
        const currentWorksheet = context.workbook.worksheets.getActiveWorksheet();
        const expensesTable = currentWorksheet.tables.add("A1:D1", true /*hasHeaders*/);
        expensesTable.name = "ExpensesTable";

        expensesTable.getHeaderRowRange().values = 
        [["Date", "Merchant", "Category", "Amount"]];

        expensesTable.rows.add(null /*add at the end*/, [
        ["1/1/2017", "The Phone Company", "Communications", "120"],
        ["1/2/2017", "Northwind Electric Cars", "Transportation", "142.33"],
        ["1/5/2017", "Best For You Organics Company", "Groceries", "27.9"],
        ["1/10/2017", "Coho Vineyard", "Restaurant", "33"],
        ["1/11/2017", "Bellows College", "Education", "350.1"],
        ["1/15/2017", "Trey Research", "Other", "135"],
        ["1/15/2017", "Best For You Organics Company", "Groceries", "97.88"]
        ]);

        expensesTable.columns.getItemAt(3).getRange().numberFormat = [['€#,##0.00']];
        expensesTable.getRange().format.autofitColumns();
        expensesTable.getRange().format.autofitRows();

        return context.sync();
    })
    .catch(function (error) {
        console.log("Error: " + error);
        if (error instanceof OfficeExtension.Error) {
            console.log("Debug info: " + JSON.stringify(error.debugInfo));
        }
    });

    postMessage("Hello World Msg!");
}

The example was made by following https://learn.microsoft.com/en-us/office/dev/add-ins/tutorials/excel-tutorial and moving the Excel.run to a webworker.


Solution

  • Your current design won't work. Excel.run() has to be in a context where Office.initialize has been called. As I understand it, you had a version that worked provided the input size was small enough. Would it be possible for you to batch the input so that each batch is processed with it's own Excel.run()? And then the next batch is handled by a new call of method that calls Excel.run().