I'd like to use a function to return a range object, but because the function is asynchronous, when my code reaches currentRangeSelection.select();
, it is still null
. How do I force the code to await the function getCurrentRangeSelection
so I can get the object instead undefined
?
EDIT 1 - Rick Kirkham: I've simplified the code so now it is possible just to paste it in the default Basic API Call (JavaScript) template in ScriptLab to test it.
After studying Promises for hours, I think I'm close to what I want, but it still doesn't work:
$("#run").click(run);
function run() {
var currentRangeSelection;
return Excel.run(function (context) {
return context.sync()
.then(function () {
return getCurrentRangeSelection(currentRangeSelection)
})
.then(function () {
// this returns an error because currentRangeSelection is undefined:
console.log("Outside the function: " + currentRangeSelection.address);
currentRangeSelection.select();
return context.sync();
})
})
.catch(function (error) {
console.log(error);
});
}
function getCurrentRangeSelection(rangeSelection) {
return Excel.run(function (context) {
return context.sync()
.then(function () {
rangeSelection = context.workbook.getSelectedRange();
rangeSelection.load();
return context.sync();
})
.then(function () {
// this works:
console.log("Inside the function: " + rangeSelection.address);
return context.sync();
});
});
}
As you can see, now the function returns a Promise
because it is (I think) the return value of Excel.run
function, and now I pass the desired return range as a parameter, but still doesn't work.
Any more tips?
EDIT 2 - Rick Kirkham
(1) I tried to create a function that returns a Promise, so I could insert it in the caller then
chain. Because of that, my only choice was to pass as reference the parameter that returns the range.
(2) Yes, I agree with you. I think (1) explained why I didn't return a range object in the first place.
(3) Yes, I fixed that.
(4) The original function was more complex and a single method from Office-JS API wouldn't return what I really wanted. I solved my problem by doing this:
$("#run").click(run);
function run() {
var testRange;
return Excel.run(function (context) {
return context.sync()
.then(function () {
testRange = getCurrentRangeSelection(context);
testRange.load();
return context.sync();
})
.then(function () {
console.log(testRange.address); // this works
return context.sync();
});
}).catch(function (error) {
console.log(error);
});
}
function getCurrentRangeSelection(context) {
var rangeSelection = context.workbook.getSelectedRange();
var activeWorksheet = context.workbook.worksheets.getActiveWorksheet();
var usedRange = activeWorksheet.getUsedRange();
return usedRange.getIntersection(rangeSelection);
}
My next question you answered with your edit: "What if I wanted the function getCurrentRangeSelection
to return an already loaded
object?"
Thanks for the example and the answers!
Try this: Redesign the getCurrentRangeSelection
function to return a Promise object. Then call the then
method of the promise object to call currentRangeSelection.select()
Edit in light of the OP's comments below and rewritten question:
Please go to this sample and see the getDocumentFilePath
function in the home.js file. Structure your getCurrentRangeSelection
method like it and pass the context object to it as a parameter. Then use the context object to get the currently selected range with the context.workbook.getSelectedRange()
method. Then pass the thing you want to return (the currently selected range, I presume) as the parameter to the resolve
method.