range.address抛出上下文相关的错误

我们已经使用Excel JavaScript API开发了好几个月了。 我们已经遇到了由于不明原因而得到解决的与上下文有关的问题。 我们无法复制这些问题,并想知道如何解决。 最近类似的问题又开始出现了。 我们一直得到的错误:

财产“地址”不可用。 在读取属性值之前,调用包含对象的load方法,并在关联的请求上下文中调用“context.sync()”。

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

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在单独的任务上asynchronous工作,例如创build表对象(表需要地址)和其他的粘贴数据到工作表(有debugging语句来查看数据被粘贴到哪里)。

这是我们的团队无法弄清楚或find解决scheme的原因。

这个代码有很多内容,但是你所遇到的问题是你不用等待以前的同步就调用同步。

这有几个问题:

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

解决方法是等待同步。 这是在TypeScript 2.1及其async/awaitfunction中最简单的做法,否则你需要做for循环的asynchronous版本,你可以查看它,但是这很不直观(需要创build一个超级承诺保持链接一堆。然后-s)

所以,你修改后的TypeScript-ified代码将会是

 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函数前面的asynchronous ,以及ContextManager.sync()loadRangeAddress前面的两个await -s。

请注意,此代码也会运行得非常慢,因为您正在为每个单元格进行asynchronous往返。 这意味着您不使用批处理 ,这是新API的对象模型的核心。

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

最后,如果我可以推荐(完全公开:我是本书的作者),你可能会在电子书“使用Office.js构buildOffice加载项”中find关于Office.js的一些有用信息,可从https://leanpub.com/buildingofficeaddins获&#x5F97; 。 具体来说,对象模型的内部工作(“ 第5.5节:实现细节,对于那些想知道它是如何工作的人 ”)有一个非常详细的(10页)部分。 它还提供了使用TypeScript的build议,有一个普遍的Promise / async-await入门,描述了.run做了什么,并且有更多关于OM的信息。 此外,尽pipe还不可用,但它很快就会提供关于如何恢复使用相同上下文的信息(使用比最初在不同的Word.run上下文中如何使用该范围所描述的技术更新的技术)。 这本书是一本精简出版的“常青”的书,儿子一旦我在未来几周写这个话题,所有现有的读者都会有更新。

希望这可以帮助!