【问题标题】:range.address throws context related errorsrange.address 引发与上下文相关的错误
【发布时间】:2017-02-23 10:48:58
【问题描述】:

几个月来,我们一直在使用 Excel JavaScript API 进行开发。我们一直在遇到与上下文相关的问题,这些问题由于未知原因得到了解决。我们无法复制这些问题,并想知道它们是如何解决的。最近类似的问题又开始出现了。 我们经常遇到的错误:

属性“地址”不可用。在阅读该物业之前 值,调用包含对象的加载方法并调用 关联请求上下文中的“context.sync()”。

我们认为,由于我们定义了多个函数来模块化项目中的代码,因此这些函数之间的上下文可能有所不同,而这些函数被忽视了。所以我们想出了通过 JavaScript 模块模式实现的单上下文解决方案。

    var ContextManager = (function () {
        var xlContext;//single context for entire project/application.
        function loadContext() {
            xlContext = new Excel.RequestContext();
        }
        function sync(object) {
            return (object === undefined) ? xlContext.sync() : xlContext.sync(object);
        }
        function getWorksheetByName(name) {
            return xlContext.workbook.worksheets.getItem(name.toString());
        }
        //public
        return {
            loadContext: loadContext,
            sync: sync,
            getWorksheetByName: getWorksheetByName
        };
    })();

注意:以上代码已缩短。添加了其他方法以确保在整个应用程序中使用单个上下文。 在实现单一上下文的同时,这一次,我们已经能够复制该问题。

    Office.initialize = function (reason) {
        $(document).ready(function () {
            ContextManager.loadContext();
            function loadRangeAddress(rng, index) {
                rng.load("address");
                ContextManager.sync().then(function () {
                    console.log("Address: " + rng.address);
                }).catch(function (e) {
                    console.log("Failed address for index: " + index);
                });
            }
            for (var i = 1; i <= 1000; i++) {
                var sheet = ContextManager.getWorksheetByName("Sheet1");
                loadRangeAddress(sheet.getRange("A" + i), i);//I expect to see a1 to a1000 addresses in console. Order doesn't matter.
            }
        });
    }

在上述情况下,只有“A1”作为范围地址打印到控制台。我看不到正在打印的任何其他地址(A2 到 A1000)。只有 catch 块执行。谁能解释为什么会这样? 尽管我已经在上面编写了 for 循环,但这不是我的用例。在实际用例中,会出现这样的情况,即函数 a 中的一个范围对象需要加载范围地址。同时,另一个函数 b 也想加载范围地址。函数 a 和函数 b 在单独的任务上异步工作,例如创建表对象(表需要地址)和其他将数据粘贴到工作表(有调试语句来查看数据粘贴到哪里)。

这是我们的团队无法弄清楚或找到解决方案的事情。

【问题讨论】:

    标签: excel office365 office-js


    【解决方案1】:

    这段代码包含很多内容,但您遇到的问题是您调用了很多次 sync 而不等待上一次同步。

    这有几个问题:

    • 如果您使用不同的上下文,您实际上会看到有大约 50 个同时请求的限制,在此之后您会收到错误。
    • 在您的情况下,您遇到了一个不同的(几乎相反的)问题。鉴于 API 的异步性质,以及您没有等待 sync-s 的事实,您的第一个 sync 请求(您认为它仅适用于 A1)实际上将包含所有 load来自执行整个for 循环的请求。现在,一旦第一个sync 被调度,动作队列将被清除。这意味着您的第二个、第三个等sync 将看到没有待处理的工作,并且不会执行任何操作,在第一次同步返回值之前执行
      • [这可能被认为是一个错误,我将与团队讨论修复它。但是,在继续使用相同上下文的下一批指令之前,不等待 sync 的完成仍然是一件非常危险的事情。]

    解决方法是等待同步。这无疑是 TypeScript 2.1 及其 async/await 功能中最简单的操作,否则您需要执行 for 循环的异步版本,您可以查找它,但它相当不直观(需要创建一个 uber-promise一直链接一堆.then-s)

    因此,您修改后的 TypeScript 化代码将是

    ContextManager.loadContext();
    async function loadRangeAddress(rng, index) {
        rng.load("address");
        await ContextManager.sync().then(function () {
            console.log("Address: " + rng.address);
        }).catch(function (e) {
            OfficeHelpers.Utilities.log(e);
        });
    }
    for (var i = 1; i <= 1000; i++) {
        var sheet = ContextManager.getWorksheetByName("Sheet1");
        await loadRangeAddress(sheet.getRange("A" + i), i);//I expect to see a1 to a1000 addresses in console. Order doesn't matter.
    }
    

    注意loadRangeAddress函数前面的async,以及ContextManager.sync()loadRangeAddress前面的两个await-s。

    请注意,此代码的运行速度也会很慢,因为您正在为每个单元格进行异步往返。这意味着您没有使用批处理,这是新 API 对象模型的核心。

    为了完整起见,我还应该注意,创建“原始”RequestContext 而不是使用Excel.run 有一些缺点。 Excel.run 做了很多有用的事情,其中​​最重要的是自动对象跟踪和取消跟踪(这里不相关,因为您只是在读回数据;但如果您正在加载然后想要写入,这将是相关的回到对象中)。

    最后,如果我可以推荐(完全披露:我是这本书的作者),您可能会在电子书“使用 Office 构建 Office 插件”中找到很多关于 Office.js 的有用信息。 js",可在https://leanpub.com/buildingofficeaddins 获得。特别是,它有一个非常详细的(10 页)部分介绍了对象模型的内部工作原理(“第 5.5 节:实现细节,供那些想知道它真正工作原理的人使用”)。它还提供了关于使用 TypeScript 的建议,有一个通用的 Promise/async-await 入门,描述了 .run 的作用,并有更多关于 OM 的信息。此外,虽然目前尚不可用,但它将很快提供有关如何恢复 使用相同上下文的信息(使用比最初在 How can a range be used across different Word.run contexts? 中描述的技术更新的技术)。这本书是一本精益出版的“常青树”书,儿子,一旦我在接下来的几周内写完该主题,所有现有读者都可以使用更新。

    希望这会有所帮助!

    【讨论】:

    • 我听说 Excel TypeScript 现在是同步的,而不是异步的。这会影响或简化您的代码吗?
    • @johnywhy,我一年多前离开了可扩展性团队——所以如果你问一个单独的问题,可能会有更多的当前人员可以回答。是的,虽然我相信这些 API / 体验仍处于预览阶段,但 API 的同步风格正在开发中(docs.microsoft.com/en-us/office/dev/scripts/overview/excel),我不确定它们是否可以在常规加载项中运行或仅在常规加载项中运行在“自动化”代码编辑器中。还有性能方面的考虑,同步 API 为了可用性而牺牲了一些性能。所以现在上面仍然是相关的
    • 知道预览版什么时候出来吗?已经快一年了。谢谢
    • @johnywhy,我没有——就像我说的,我已经从那个团队转到了微软内部完全不同的地方。看起来office-scripts有一个stackoverflow标签,我建议你在那里问(也可以在这里的cmets中链接到你的问题,以便其他人可以关注讨论):stackoverflow.com/questions/tagged/office-scripts
    猜你喜欢
    • 2022-10-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-03
    • 2012-01-04
    • 2016-09-08
    • 1970-01-01
    • 2017-07-25
    • 1970-01-01
    相关资源
    最近更新 更多