gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-02 19:21:26 25 4
gpt4 key购买 nike

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

property 'address' is not available. Before reading the property's value, call the load method on the containing object and call "context.sync()" on the associated request context.

我们认为,由于我们定义了多个函数来模块化项目中的代码,因此这些函数之间的上下文可能有所不同,而这些函数未被注意到。因此,我们想出了通过 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 block 。谁能解释为什么会发生这种情况?尽管我在上面编写了 for 循环,但这不是我的用例。在实际用例中,会发生这样的情况:函数 a 中的一个范围对象需要加载范围地址。与此同时,另一个函数 b 也想加载范围地址。函数 a 和函数 b 都在不同的任务上异步工作,例如一个创建表对象(表需要地址),另一个将数据粘贴到工作表(有调试语句来查看数据粘贴的位置)。

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

最佳答案

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

这有几个问题:

  • 如果您使用不同上下文,您实际上会发现并发请求的限制为约 50 个,超过此限制您就会收到错误。
  • 就您而言,您遇到了一个不同的(几乎相反的)问题。考虑到 API 的异步性质,以及您没有等待 sync-s 的事实,您的第一个 sync 请求(您认为只是为了A1) 实际上将包含整个 for 循环执行过程中的所有 load 请求。现在,一旦调度第一个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() 前面的两个 await -s 和 loadRangeAddress

请注意,此代码的运行速度也会相当慢,因为您要为每个单元格进行异步往返。这意味着您没有使用批处理,而批处理是新 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 - range.address 抛出上下文相关的错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42413573/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com