Excel.run,传和returncontext作为变量,context从哪里来?
Excel.run, pass and return context as a variable, where does context come from?
我有一个很大的 Google Apps Script 项目在 Google 电子表格上工作,我正试图将它转换为一个在 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()
?
- 在这种情况下是否生成了新的第二个上下文,它会发生什么?
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();
});
- 如果我把我的主要代码放在 Excel.run 里面,然后在其他函数中传递 return
context: Excel.RequestContext
和 range: 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
});
- 此外,如果我每次需要值时都必须显式等待,那么异步函数的优势是什么?我的意思是,如果我要有节制地使用
context.sync()
,那意味着我只在迫切需要它时才使用它,所以它必须始终与 await
一起使用。那么为什么不让 context.sync()
默认同步呢?
我将尝试回答其中一些问题,并尝试为其他问题寻求帮助。我也推荐这本书 Building Office Add-ins for an understanding of the Office JavaScript library. See this too, if you haven't already: Application specific API model.
1a。是的。这基本上是正确的。在幕后,Excel.run
创建一个 Office.RequestContext
对象并将其传递给批处理函数参数。 (但是你的两个代码块在字面上并不等同。你不会调用 Excel.run
并显式创建一个 RequestContext
对象。)
1b。通过浏览我链接到的书,我 认为 你必须 return
书上所说的 meta-promise 以便 Excel.run
可以解决答应它returns。这是书中的一个例子:
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);
通过浏览我链接的书,我认为答案是肯定的; Excel.run
总是创建一个新的 context
对象并将其传递给批处理函数。有一些技术和 Excel.run
的覆盖使您能够将在一个上下文中创建的对象传递给 Excel.run
的另一个调用,但这些是为了与 Excel.run
的独立调用一起使用,而不是嵌套调用,如您的情况。
没有。您不应该在 a
或 b
.
中调用 Excel.run
我认为有些情况下您不需要等待context.sync
。例如,当 context.sync
之后的父函数中的所有代码仅影响任务窗格的 UI,而不依赖于从当前 Office 文档中读取任何数据。尽量减少 context.sync
调用的最佳做法是因为它需要在文档和 JavaScript 运行时之间有一个 round-trip,其中 add-in 代码是 运行(在用户的计算机)。无论 context.sync
是否同步,这都是正确的。
对于 1a,这是我从 ScriptLab 的智能感知中获得的关于 运行 函数如何工作的描述之一:
A function that takes in a RequestContext and returns a promise (typically, just the result of "context.sync()"). The context parameter facilitates requests to the Excel application. Since the Office add-in and the Excel application run in two different processes, the RequestContext is required to get access to the Excel object model from the add-in.
就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 中非常常见。所以这可能被认为是一种不好的做法。
我有一个很大的 Google Apps Script 项目在 Google 电子表格上工作,我正试图将它转换为一个在 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()
?
- 在这种情况下是否生成了新的第二个上下文,它会发生什么?
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();
});
- 如果我把我的主要代码放在 Excel.run 里面,然后在其他函数中传递 return
context: Excel.RequestContext
和range: 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
});
- 此外,如果我每次需要值时都必须显式等待,那么异步函数的优势是什么?我的意思是,如果我要有节制地使用
context.sync()
,那意味着我只在迫切需要它时才使用它,所以它必须始终与await
一起使用。那么为什么不让context.sync()
默认同步呢?
我将尝试回答其中一些问题,并尝试为其他问题寻求帮助。我也推荐这本书 Building Office Add-ins for an understanding of the Office JavaScript library. See this too, if you haven't already: Application specific API model.
1a。是的。这基本上是正确的。在幕后,Excel.run
创建一个 Office.RequestContext
对象并将其传递给批处理函数参数。 (但是你的两个代码块在字面上并不等同。你不会调用 Excel.run
并显式创建一个 RequestContext
对象。)
1b。通过浏览我链接到的书,我 认为 你必须 return
书上所说的 meta-promise 以便 Excel.run
可以解决答应它returns。这是书中的一个例子:
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);
通过浏览我链接的书,我认为答案是肯定的;
Excel.run
总是创建一个新的context
对象并将其传递给批处理函数。有一些技术和Excel.run
的覆盖使您能够将在一个上下文中创建的对象传递给Excel.run
的另一个调用,但这些是为了与Excel.run
的独立调用一起使用,而不是嵌套调用,如您的情况。没有。您不应该在
中调用a
或b
.Excel.run
我认为有些情况下您不需要等待
context.sync
。例如,当context.sync
之后的父函数中的所有代码仅影响任务窗格的 UI,而不依赖于从当前 Office 文档中读取任何数据。尽量减少context.sync
调用的最佳做法是因为它需要在文档和 JavaScript 运行时之间有一个 round-trip,其中 add-in 代码是 运行(在用户的计算机)。无论context.sync
是否同步,这都是正确的。
对于 1a,这是我从 ScriptLab 的智能感知中获得的关于 运行 函数如何工作的描述之一:
A function that takes in a RequestContext and returns a promise (typically, just the result of "context.sync()"). The context parameter facilitates requests to the Excel application. Since the Office add-in and the Excel application run in two different processes, the RequestContext is required to get access to the Excel object model from the add-in.
就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 中非常常见。所以这可能被认为是一种不好的做法。