I am developing an Excel add-in using the Office JS API. I have a taskpane which is running code similar to the following:
var failA = true
var failB = true
// In reality this is Excel.run, and it injects a context
async function run(f) {
f()
}
// Simulate a failing async call
async function fail(message, delay) {
setTimeout(()=>{
throw new Error(message)
}, delay)
}
// Simulate a successful async call
async function success(message, delay) {
setTimeout(delay)
}
async function doA() {
console.log("Inside A");
if (failA) {
console.log("Failing A");
await fail("Error A", 1000)
} else {
success("Success A")
}
console.log("Done A")
}
async function doB() {
console.log("Inside B");
if (failB) {
console.log("Failing B");
await fail("Error B", 1000)
} else {
success("Success B")
}
console.log("Done B")
}
async function main () {
try {
// This is how Excel.run is called in all the Office samples
await run(async ()=>{
console.log("Start main");
await doA();
console.log("Between A and B");
await doB();
console.log("Finished");
})}
catch (error) {
console.log("ERROR: " + error.message)
}
}
// Need to await main in an async context. In reality main is run from a button
(async () => await main())()
.as-console-wrapper { min-height: 100%!important; top: 0; }
I would expect the error in doA
to bubble up and interrupt further execution of doB
. The output should then be:
Start main
Inside A
Failing A
ERROR: Error A
Instead what I get is:
Start main
Inside A
Failing A
Done A
Between A and B
Inside B
Failing B
Done B
Finished
followed by two uncaught exceptions Error A
and Error B
.
What am I doing wrong? Can I achieve the behavior I expect without wrapping doA
and doB
separately in try...catch
blocks?
2
Answers
setTimeout
does not know anything about promises. Instead use a promise based sleep for waiting.Also you forgot to
await f()
in yourrun
function, or at least return it to keep promise chain intact.There are a few issues:
fail
is not simulating a failing async call. It returns an immediately fulfilling promise. ThesetTimeout
callback that will later throw an error, is irrelevant for that promise, since that callback executes from a fresh call stack. Similarly,success
is initiating asetTimeout
that has no relevance to the promise it returns, which again gets fulfilled immediately.run
is not linking its resolution to the fate of the promise returned byf
and thusrun
returns a promise that fulfills whilef
is not monitored for errors. Iff
rejects,run
will not have captured it.If you configure your script to execute
success
then you need toawait
it, otherwise its delay will have no effect.Here is a fix: