Excel.run,将上下文作为变量传递和返回,上下文从哪里来



我有一个在谷歌电子表格上工作的大型谷歌应用程序脚本项目,我正试图将其转换为在Excel工作簿上工作的office js插件。我知道,将调用excel特定函数(直接与工作簿交互(的所有内容放入Excel.run()函数中是一种很好的做法,这样它可以进行适当的清理,不会发生内存泄漏。我也知道我应该尽可能少地执行context.sync()以优化性能。

以下是我的问题,(我认为其中一些问题来自于我对js如何工作的不完全理解;这些事情GAS在我不需要质疑的情况下处理(:

1a(当我们将代码块放入时

Excel.run(context => {
//code that does stuff with context; 
context.sync();
});

上下文来自哪里?这相当于吗

Excel.run(()=> {
let context = new Excel.RequestContext; 
//code that does stuff with context; 
context.sync();
});

1b(此外,如果每个新函数都生成上下文,为什么我会使用return context.sync()而不仅仅是context.sync()

  1. 在这种情况下是否生成了新的第二个上下文,它会发生什么
function handle_error(e){//second context generated in case of error
let context=New Excel.RequestContext;
context.workbook.load('name');
await context.sync();
some_logging_function(context.workbook.name, e);
}
function data_func(some_data: Non-Excel-Interface): Other-Non-Excel-Interface{
//manipulate data
//in case of error
handle_error(e);
//continue with data massaging
return altered_data;
}
Excel.run(context=>{ //first context
context.workbook.worksheets.getItem('Sheet1').getUsedRange().load('values');
context.sync();
let values = context.workbook.worksheets.getItem('Sheet1').getUsedRange().values;
let some_data: Non-Excel-Interface = {sheetName: 'Sheet1', data: values};
let new_vals = data_func(some_data);
context.workbook.worksheets.getItem('Sheet1').getUsedRange().values = new_vals.new_data;
context.sync();
});
  1. 如果我把主代码放在Excel.run中,然后在其他函数中传递并返回context: Excel.RequestContextrange: Excel.Range。我在这些函数中也需要Excel.run()吗?换句话说,函数a()b()内部的代码应该在Excel.run()内部吗
function a(rng: Excel.Range, values:string[][]):Excel.Range{
rng.values = values;
return rng;
}
function b(context: Excel.RequestContext): Excel.RequestContext{
context.workbook.load('name');//load name property, but don't context.sync()
return context;
}
Excel.run(async context=>{
context = b(context);
let rng =  context.workbook.worksheets.getItem('Sheet1').getUsedRange();
rng.load('values');
await context.sync();//values property and workbook name property must be available now
rng = a(rng, [['aa', 'bb', 'cc']]);
await context.sync();//new values must be available now
console.log(context.workbook.name, rng.values);//should show the title of the workbook and the newly assigned values of the range
});
  1. 此外,如果每次需要值时都必须显式等待,那么异步函数的优势是什么?我的意思是,如果我要少量使用context.sync(),那意味着我只在急需的时候使用它,所以它必须始终与await一起使用。那么,为什么不默认使context.sync()同步呢

我将尝试回答其中一些问题,并尝试为其他问题寻求帮助。我还推荐《构建Office外接程序》一书来了解Office JavaScript库。如果您还没有:应用程序特定的API模型,请参阅此内容。

1a。对这基本上是正确的。在后台,Excel.run创建一个Office.RequestContext对象,并将其传递给批处理函数参数。(但您的两个代码块在字面上并不等效。您不会调用Excel.run并显式创建RequestContext对象。(

1b。从浏览我链接到的书中,我认为您必须return,书中称之为元承诺,这样Excel.run才能解析它返回的承诺。这是书中的一个例子:

Excel.run(function (context) {
var selectionRange = context.workbook.getSelectedRange();
selectionRange.format.fill.clear();
selectionRange.load("values");

return context.sync()
.then(function () {
var rowCount = selectionRange.values.length;
var columnCount = selectionRange.values[0].length;
for (var row = 0; row < rowCount; row++) {
for (var column = 0; column < columnCount; column ++) {
if (selectionRange.values[row][column] > 50) {
selectionRange.getCell(row, column)
.format.fill.color = "yellow";
}
}
}
})
.then(context.sync);

}).catch(OfficeHelpers.Utilities.log);
  1. 通过浏览我链接的书,我认为答案是肯定的;CCD_ 20总是创建一个新的CCD_。有一些技术和Excel.run的覆盖使您能够将在一个上下文中创建的对象传递给Excel.run的另一个调用,但这些技术和覆盖用于Excel.run的独立调用,而不是像您的情况那样用于嵌套调用。

  2. 没有。您不应该在ab中调用Excel.run

  3. 我认为在某些情况下,您不需要等待context.sync。例如,当context.sync之后的父函数中的所有代码仅影响任务窗格的UI,而不依赖于从当前Office文档中读取任何数据时。最大限度地减少context.sync调用的好做法是,它需要在文档和JavaScript运行时之间往返,插件代码在运行时(在用户的计算机上(运行。无论context.sync是否同步,这都是正确的。

对于1a,以下是我从ScriptLab的intelligense:获得的Run函数如何工作的描述之一

一个函数,它接受RequestContext并返回promise(通常只是"context.sync(("的结果(。上下文参数方便了对Excel应用程序的请求。由于Office外接程序和Excel应用程序在两个不同的进程中运行,因此需要RequestContext才能从外接程序访问Excel对象模型。

就1b而言,我不这么认为。RequestContext似乎不是一个你可以自己实例化的对象

编辑:事实上,这看起来确实是可能的。请参阅以下内容:

$("#run").click(() => tryCatch(run));
async function run() {
await Excel.run(async () => {
let ctx:Excel.RequestContext = new Excel.RequestContext();
let wb: Excel.Workbook = ctx.workbook
let rang: Excel.Range = wb.getSelectedRange()
rang.load("address")
await ctx.sync()
console.log(rang.address)
});
}
/** Default helper for invoking an action and handling errors. */
async function tryCatch(callback) {
try {
await callback();
} catch (error) {
// Note: In a production add-in, you'd want to notify the user through your add-in's UI.
console.error(error);
}
}

我建议不要使用这种方法。在JavaScript中,传递匿名函数(如原始示例中所示(非常常见。因此,这可能被认为是一种糟糕的做法。

最新更新