在 JavaScript API for Office 中使用承诺 (ctx.sync) 编写循环的最佳方式
Best way to write loops with promises (ctx.sync) in JavaScript API for Office
有很多线程在讨论保证循环中承诺的执行顺序。我想知道 JavaScript API 中 Office 加载项的最佳做法是什么。大多数时候,有问题的承诺是 ctx.sync()
.
这是一个片段,用于逐个打印 Excel 范围列表的地址。测试表明它很好地遵守了 Excel 范围的顺序。但问题是是否以及如何保证执行顺序?
function loadAll () {
var ranges = ["A:A", "B:B", "C:C", "D:D", "E:E"];
var sheet = "Sheet1";
for (var i = 0; i < ranges.length; i++) {
loadRange(ranges[i], sheet);
}
}
function loadRange (range, sheet) {
Excel.run(function (ctx) {
var r = ctx.workbook.worksheets.getItem(sheet).getRange(range);
r.load('address');
return ctx.sync().then(function() {
console.log(r.address);
});
});
}
有人能帮忙吗?
因为 Excel.run
returns 一个 Promise,你可以用 .then
链接它并保证顺序。即,
Excel.run(function(ctx) { ... return ctx.sync(); ... })
.then(function() {
return Excel.run(function(ctx) { ... return ctx.sync(); ... })
})
.then(function() {
return Excel.run(function(ctx) { ... return ctx.sync(); ... })
});
话虽这么说……这会非常低效。一种更好的方法是在一批中加载所有你需要的对象,只创建一个网络往返(对于 Excel 在线尤其重要......但即使在桌面上也很明显):
function loadAll () {
Excel.run(function(ctx) {
var ranges = ["A:A", "B:B", "C:C", "D:D", "E:E"];
var sheet = "Sheet1";
var loadedRanges = [];
for (var i = 0; i < ranges.length; i++) {
var r = ctx.workbook.worksheets.getItem(sheet).getRange(ranges[i]);
r.load('address');
loadedRange.push(r);
}
return ctx.sync()
.then(function() {
for (var i = 0; i < loadedRanges.length; i++) {
console.log(loadedRanges[i].address);
}
});
});
}
更新
如果根据评论,您最终确实需要执行相互依赖且每个任务都需要往返的单独任务,因此需要通过链接 Excel.run
进行排序,我会建议内容如下:
function loadAll () {
var ranges = ["A:A", "B:B", "C:C", "D:D", "E:E"];
var sheet = "Sheet1";
// Create a starter promise object
var promise = new OfficeExtension.Promise(function(resolve, reject) { resolve (null); });
for (var i = 0; i < ranges.length; i++) {
// Create a closure over i, since it's used inside a function that won't be immediately executed.
(function(i) {
// Chain the promise by appending to it:
promise = promise.then(function() {
return loadRange(ranges[i], sheet);
})
})(i);
}
}
function loadRange (range, sheet) {
return Excel.run(function (ctx) {
var r = ctx.workbook.worksheets.getItem(sheet).getRange(range);
r.load('address');
return ctx.sync().then(function() {
console.log(r.address);
});
});
}
~ Michael Zlatkovsky,Office 可扩展性团队开发人员,MSFT
@Michael Zlatkovsky 的更新完美解决了附加promise的问题。
@SoftTimur添加的补码允许等待所有的promise完成后再做一个.then(),也很方便!
我对这些帖子的唯一评论是,如果任何 promises 抛出错误,其他附加的 promises 将停止处理。
就我而言,情况有点不同。澄清一下:
Excel.run(function(context){
return runWorkbook(context, context.workbook)
.then(function(){ var cool = "all promises worked !" }
.catch(function(error)) { var bad = "do not want to be here :(" });
}
function runWorkbook(context, workbook){
const sheets = workbook.worksheets;
sheets.load("$none");
return context.sync().then(function(){
let promise = new window.OfficeExtension.Promise(function(resolve, reject) { resolve(null); });
sheets.items.forEach(function(ws) {
promise = promise.then(function() {
return makeWorkOnWorksheet(ws)
.then(context.sync())
.catch(function(error)){
// DO NOTHING BUT CAN NOT THROW ERROR OTHERWISE IT BREAKS THE NEXT APPENDED PROMISES
});
}
return promise;
}
}
这个解决方案有效..(捕捉评论中的错误并且什么也不做)
我不喜欢这个解决方案,但这是我发现允许完成所有附加承诺的唯一方法。
如果有人有更好的想法,欢迎提出来;)
干杯,
有很多线程在讨论保证循环中承诺的执行顺序。我想知道 JavaScript API 中 Office 加载项的最佳做法是什么。大多数时候,有问题的承诺是 ctx.sync()
.
这是一个片段,用于逐个打印 Excel 范围列表的地址。测试表明它很好地遵守了 Excel 范围的顺序。但问题是是否以及如何保证执行顺序?
function loadAll () {
var ranges = ["A:A", "B:B", "C:C", "D:D", "E:E"];
var sheet = "Sheet1";
for (var i = 0; i < ranges.length; i++) {
loadRange(ranges[i], sheet);
}
}
function loadRange (range, sheet) {
Excel.run(function (ctx) {
var r = ctx.workbook.worksheets.getItem(sheet).getRange(range);
r.load('address');
return ctx.sync().then(function() {
console.log(r.address);
});
});
}
有人能帮忙吗?
因为 Excel.run
returns 一个 Promise,你可以用 .then
链接它并保证顺序。即,
Excel.run(function(ctx) { ... return ctx.sync(); ... })
.then(function() {
return Excel.run(function(ctx) { ... return ctx.sync(); ... })
})
.then(function() {
return Excel.run(function(ctx) { ... return ctx.sync(); ... })
});
话虽这么说……这会非常低效。一种更好的方法是在一批中加载所有你需要的对象,只创建一个网络往返(对于 Excel 在线尤其重要......但即使在桌面上也很明显):
function loadAll () {
Excel.run(function(ctx) {
var ranges = ["A:A", "B:B", "C:C", "D:D", "E:E"];
var sheet = "Sheet1";
var loadedRanges = [];
for (var i = 0; i < ranges.length; i++) {
var r = ctx.workbook.worksheets.getItem(sheet).getRange(ranges[i]);
r.load('address');
loadedRange.push(r);
}
return ctx.sync()
.then(function() {
for (var i = 0; i < loadedRanges.length; i++) {
console.log(loadedRanges[i].address);
}
});
});
}
更新
如果根据评论,您最终确实需要执行相互依赖且每个任务都需要往返的单独任务,因此需要通过链接 Excel.run
进行排序,我会建议内容如下:
function loadAll () {
var ranges = ["A:A", "B:B", "C:C", "D:D", "E:E"];
var sheet = "Sheet1";
// Create a starter promise object
var promise = new OfficeExtension.Promise(function(resolve, reject) { resolve (null); });
for (var i = 0; i < ranges.length; i++) {
// Create a closure over i, since it's used inside a function that won't be immediately executed.
(function(i) {
// Chain the promise by appending to it:
promise = promise.then(function() {
return loadRange(ranges[i], sheet);
})
})(i);
}
}
function loadRange (range, sheet) {
return Excel.run(function (ctx) {
var r = ctx.workbook.worksheets.getItem(sheet).getRange(range);
r.load('address');
return ctx.sync().then(function() {
console.log(r.address);
});
});
}
~ Michael Zlatkovsky,Office 可扩展性团队开发人员,MSFT
@Michael Zlatkovsky 的更新完美解决了附加promise的问题。 @SoftTimur添加的补码允许等待所有的promise完成后再做一个.then(),也很方便!
我对这些帖子的唯一评论是,如果任何 promises 抛出错误,其他附加的 promises 将停止处理。
就我而言,情况有点不同。澄清一下:
Excel.run(function(context){
return runWorkbook(context, context.workbook)
.then(function(){ var cool = "all promises worked !" }
.catch(function(error)) { var bad = "do not want to be here :(" });
}
function runWorkbook(context, workbook){
const sheets = workbook.worksheets;
sheets.load("$none");
return context.sync().then(function(){
let promise = new window.OfficeExtension.Promise(function(resolve, reject) { resolve(null); });
sheets.items.forEach(function(ws) {
promise = promise.then(function() {
return makeWorkOnWorksheet(ws)
.then(context.sync())
.catch(function(error)){
// DO NOTHING BUT CAN NOT THROW ERROR OTHERWISE IT BREAKS THE NEXT APPENDED PROMISES
});
}
return promise;
}
}
这个解决方案有效..(捕捉评论中的错误并且什么也不做)
我不喜欢这个解决方案,但这是我发现允许完成所有附加承诺的唯一方法。
如果有人有更好的想法,欢迎提出来;)
干杯,