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()

  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 里面,然后在其他函数中传递 return 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() 默认同步呢?

我将尝试回答其中一些问题,并尝试为其他问题寻求帮助。我也推荐这本书 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);
  1. 通过浏览我链接的书,我认为答案是肯定的; Excel.run 总是创建一个新的 context 对象并将其传递给批处理函数。有一些技术和 Excel.run 的覆盖使您能够将在一个上下文中创建的对象传递给 Excel.run 的另一个调用,但这些是为了与 Excel.run 的独立调用一起使用,而不是嵌套调用,如您的情况。

  2. 没有。您不应该在 ab.

    中调用 Excel.run
  3. 认为有些情况下您不需要等待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 中非常常见。所以这可能被认为是一种不好的做法。