【问题标题】:Chaining Promises in Office.js with Excel.run()使用 Excel.run() 在 Office.js 中链接 Promise
【发布时间】:2015-09-25 16:22:33
【问题描述】:

我正在使用新的 office.js。我正在使用返回承诺的 Excel.run 功能。我对图书馆实现的承诺模式有疑问。

样本都显示出这种模式

Excel.run( function (ctx) {

  //set up something

  return ctx.sync().then (function () {
    //call another function somewhere to chain operations
  });

}).then ( function () {
  //do something else if you want
}).catch (function (error) {
  handle errors
});

问题在于 Excel.run() 中包含的 ctx.sync().then() 它的呈现方式,您不能按照 Promise 规范链接 Promise,因为如果您尝试在 Excel.run() 之外处理 then(),则会丢失上下文对象 因此,该模式似乎在促进嵌套函数调用,而这正是 Promise 应该消除的。

我想要做的是通过这样的链接将多个调用排序在一起:

Excel.run( function (ctx) {
  return ctx.sync();
}).then ( function (ctx) {
  return ctx.sync();
}).then ( function (ctx) {
  return ctx.sync();
}).then ( function (ctx) {
  return ctx.sync();
}).catch (function (error) {

});

这可能吗?

【问题讨论】:

    标签: excel ms-office office365 office-js


    【解决方案1】:

    一般来说,Excel.run 的目的是针对 OM 进行顺序操作,并在最后进行自动清理。也就是说,Excel.run 创建一个上下文,运行您的操作,然后清理所有已分配的主机对象。

    话虽如此,正如 Gab Royer 所说,您可以将对象传递出去。此外,每个 Excel 对象都有一个通过“.context”属性指向其“上下文”的反向指针。例如,您可以这样做:

    Excel.run(function (ctx) {
        var worksheet = ctx.workbook.worksheets.getActiveWorksheet();
        return ctx.sync(worksheet);
    }).then(function(worksheet) {
        worksheet.name = "Test"
        return worksheet.context.sync();
    }).catch(function(e) {
        console.log(e)  
    });
    

    如您所见,在上面的代码中,您在Excel.run 内创建了工作表对象,但在外部使用它。

    如果你有一个 Range 对象,它会变得有点棘手。范围与工作表不同,没有具有持久性 ID(它们怎么可能?基本上所有可能的单元格组合有无数种排列方式)。相反,在Excel.run 期间,我们自动创建指向支持 Range 对象的持久指针,这些对象由 Excel 调整和跟踪。当Excel.run 内部的批处理完成时,我们告诉主机销毁这些引用。所以如果你有这样的代码:

    Excel.run(function (ctx) {
        var range = ctx.workbook.getSelectedRange();
        return ctx.sync(range);
    }).then(function(range) {
        range.format.fill.color = "red";
        return ctx.sync();
    }).catch(function(e) {
        console.log(e)  
    })
    

    它会遇到“InvalidObjectPath”错误。

    但是,您可以通过手动将对象添加到 ctx.trackedObjects 集合来选择退出跟踪对象清理。然而,在这样做的时候,你自己承担了最后清理的责任——你需要格外小心,记住不仅要清理成功,还要清理失败。否则,您实际上是在造成内存泄漏,这将继续降低 Excel 主机应用程序的速度。

    var range;
    Excel.run(function (ctx) {
        range = ctx.workbook.getSelectedRange();
        ctx.trackedObjects.add(range);
        return ctx.sync(range);
    }).then(function(range) {
        range.format.fill.color = "red";
        return range.context.sync();
    }).then(function() {
        // Attempt to clean up any orphaned references
        range.context.trackedObjects.remove(range);
        range.context.sync(); // don't need to await it, since it's just the final cleanup call
    }).catch(function(e) {
        console.log(e);
    })
    

    长话短说:这当然是可行的,你可以在Excel.run 之后使用对象。您只需要负责任何需要“跟踪”的对象的内存管理。在上面的示例中,没有理由来进行这项工作,因为您也可以在 Excel.run 中拥有相同的代码(请记住,您可以在 Excel.run 内部的批处理也是 - 无需在外部执行此操作)。但是,如果您有一个场景,例如,您有一个需要经常运行的计时器作业(例如,更新股票代码),或者您想为特定对象创建一个带有 onclick 处理程序的按钮等. 上面的技术可以让你在 Excel.run 中创建对象,然后在它之外使用它们。

    PS:关于需要嵌套的模式:确实,如果你需要在 Excel.run 中链接 ctx.sync() 调用,你最终会得到一层嵌套——但是只是一个 single 额外的层。在内部,您仍然可以在没有回调金字塔的情况下链接您的承诺。例如:

    Excel.run(function (ctx) {
        var range = ctx.workbook.worksheets.getActiveWorksheet().getRange("A1:C3");
        range.load("values");
        return ctx.sync()
            .then(function () {
                // Some set of actions against the OM, now that the "values"
                // property has been loaded and can be read from the "range" object.
            })
            .then(ctx.sync)
            .then(function () {
                // Another set of actions against the OM, presumably after doing
                // another load-requiring operation (otherwise could have
                // been part of the same .then as above)
            })
            .then(ctx.sync)
            .then(function() {
                // One final set of actions
            });     
    }).catch(function(error) {
        console.log("Error: " + error);
    });
    

    【讨论】:

    • 这应该直接复制到文档中。出色的洞察力。谢谢!
    • 所以你提到了这个:“你想为特定对象创建一个带有 onclick 处理程序的按钮,等等。上面的技术将让你在 Excel.run 中创建对象,然后使用它们在它之外。”我正在做这件事,但我发现如果我在我的对象主体上调用.select(),我的对象没有滚动到或突出显示,但我没有收到任何异常。我有一个带有表格的 Word 文档。我添加对特定单元格的引用,将其添加为跟踪对象,并附加事件处理程序。但是点击时没有任何反应。这对你有用吗?
    • 对于遇到此问题的任何人,我都误解了上下文同步的本质。在我的初始上下文之外,我必须打开一个新的Word.run 来获取一个新的上下文,然后在执行我的select() 之后在我的实际trackedObject 上调用context.sync()。我认为setTimeOut 示例中的说明很好,我只是错过了。
    【解决方案2】:

    虽然这是可能的,因为Excel.RequestContext.sync 接受传递值,Excel.run 的目标是管理它传入的函数的 trackedObjects。在Excel.Run 之后链接的承诺中,你必须自己管理 trackedObjects,从而违背了 Excel.Run 的目的。

    如果您不喜欢添加的缩进,我建议您在 Excel.Run 之外声明您的函数,或者创建您自己的 RequestContext 对象。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-04-27
      • 1970-01-01
      • 1970-01-01
      • 2016-11-27
      • 1970-01-01
      • 1970-01-01
      • 2013-07-04
      相关资源
      最近更新 更多