在 Excel 中使用 Office.js 的竞争条件

Race conditions using Office.js in Excel

我在尝试将 HTTP 请求的响应写入活动单元格的以下代码中遇到了某种竞争条件。我已经从 Office.js 中阅读了 "InvalidObjectPath" 错误的一些可能解决方案(我专门使用 ScriptLab),但我不认为我正在尝试在多个上下文中使用任何东西。

当前行为有时有效,但有时不会向单元格写入任何内容。

var counter = 0;
$("#run").click(run);
async function run() {
    try {

        await Excel.run(async (ctx) => {
            var user; 
            const sUrl = "https://jsonplaceholder.typicode.com/users/1";
            var client = new HttpClient();
            var range = ctx.workbook.getSelectedRange(); 
            counter++;
            client.get(sUrl, function (response) {
                var obj = JSON.parse(response);
                user = obj.username;
                range.values = [[user + counter]];
                ctx.sync();
            });
            await ctx.sync();
        });
    }
    catch (error) {
        OfficeHelpers.UI.notify(error);
        OfficeHelpers.Utilities.log(error);
    }
}

var HttpClient = function() {
    this.get = function(aUrl, aCallback) {
        var anHttpRequest = new XMLHttpRequest();
        anHttpRequest.onreadystatechange = function() { 
            if (anHttpRequest.readyState == 4 && anHttpRequest.status == 200)
                aCallback(anHttpRequest.responseText);
        }
        anHttpRequest.open( "GET", aUrl, true );            
        anHttpRequest.send(null);
    }
}

问题是您没有等待 client.get 完成。这意味着 [有时],Excel.run 将完成并且 "garbage-collect"(ish) 一些对象 (range) 在执行 client.get 内部的回调之前.

您可以通过多种方式解决问题:

  1. 在执行 Excel.run 之前调用 Web 服务。在此处的示例中(对于许多其他场景可能不现实,但它在这里),在进行网络调用之前,您实际上根本不依赖文档中的任何内容。在这种情况下,根本不需要在 Excel.run 内,您可以让 Excel.run 成为 Web 服务调用回调的一部分。

  2. 将您的 Web 服务调用包装在 Promise 中,以便等待它。像这样: var HttpClient = function() { this.get = function(aUrl) { return new Promise(function (resolve, reject) { var anHttpRequest = new XMLHttpRequest(); anHttpRequest.onreadystatechange = function () { if (anHttpRequest.readyState == 4 && anHttpRequest.status == 200) { resolve(anHttpRequest.responseText); } else { reject(anHttpRequest.statusText); } } anHttpRequest.open("GET", aUrl, true); anHttpRequest.send(null); }); } }

我在一本关于使用 Office.js 构建 Office 加载项的书中描述了这两种方法(以及更多...):https://leanpub.com/buildingofficeaddins/。我在下面粘贴了一些相关书籍内容的截图。

顺便说一句,我应该说获得选择是您不想延迟 sync 的少数几次之一,因为您想要捕捉转瞬即逝的时间点选择而不是一旦网络调用成功,从现在起 X 秒后将成为选择的内容。因此,这是您可能想要插入额外 await context.sync() 的少数情况之一,即使您在技术上不需要它。有关详细信息,请参阅书中的“5.8.2:何时同步”部分。

=====

承诺 API:

=====

关于Promises:

=====

来自实施细节部分: