When I first read your post, I thought that there must be a solution, especially as we can now run asynchronous code, but I couldn’t get it to work until I discovered that if I put a delay (even of a millisecond) on the Resolve callback of a Promise, the ui would redraw (and therefore the progress bar would update).
This is the critical piece of code:
return new Promise(function (resolve, reject) {
// do whatever . . .
setTimeout(function () {
resolve();
}, 1);
});
To be honest, I don’t know why this works, but it does, and I think it gives us a simple way to update the progress bar as and when we need to.
As a proof of concept, I have made a test plugin.
Because we want the ui to redraw when we update the progress bar I have two functions, one to reset the progress bar (set its value to zero) and one to increment the progress bar (advance its value). Each function wraps the setting of the progress bar’s value in a Promise which includes the ‘Resolve trigger’.
const progressBar = document.getElementById('progress-bar');
function resetProgress() {
return new Promise(function (resolve, reject) {
progressBar.value = 0;
setTimeout(function () { // millisecond pause triggers refresh of ui
resolve();
}, 1);
});
}
function incrementProgress() {
return new Promise(function (resolve, reject) {
progressBar.value = progressBar.value + 1;
setTimeout(function () { // millisecond pause triggers refresh of ui
resolve();
}, 1);
});
}
I have three buttons: ‘promise’, ‘async’ and ‘loop’. Following your dummy plugin, clicking on any one of my three buttons has InDesign create a new document and put a text frame on the first page.
Then, to simulate a long-running process, I have a little function that in a loop moves an object (in this case the text frame) one unit to the right and then one unit to the left (i.e. back to where it started). Completely useless in the real world, but when looping about 250 times it keeps InDesign occupied long enough for us to see the progress bar move slowly.
function slowMove(obj, numMoves) {
for (let i = 0; i < numMoves; i++) {
obj.move([1,0]);
obj.move([-1,0]);
}
}
The button ‘promise’ shows how this all comes together with a Promise chain:
resetProgress()
.then(function() {
slowMove(textFrame, numMoves);
return incrementProgress();
}).then(function() {
slowMove(textFrame, numMoves);
return incrementProgress();
}).then(function() {
slowMove(textFrame, numMoves);
return incrementProgress();
}).then(function() {
slowMove(textFrame, numMoves);
return incrementProgress();
}).then(function() {
slowMove(textFrame, numMoves);
return incrementProgress();
});
The progress bar moves to 20%, 40%, 60%, 80%, 100% when each incrementProgress() is reached.
The button ‘async’ shows how this can be used with async/await syntax (note that with async/await the actions are all put in an asynchronous function called from the ‘click’ handler).
async function run(obj, numMoves) {
await resetProgress();
slowMove(obj, numMoves);
await incrementProgress();
slowMove(obj, numMoves);
await incrementProgress();
slowMove(obj, numMoves);
await incrementProgress();
slowMove(obj, numMoves);
await incrementProgress();
slowMove(obj, numMoves);
await incrementProgress();
}
The progress bar moves as it does when the ‘promise’ button is clicked.
In practice, a progress bar is often combined with code performed in a loop, so the ‘loop’ button shows how this might be done.
In the ‘click’ handler:
let numMoves = 10;
setProgressMax(100);
runLoop(textFrame, numMoves);
Calling this function:
async function runLoop(obj, numMoves) {
let numLoops = progressBar.max;
await resetProgress();
for (let i = 0; i < numLoops; i++) {
slowMove(obj, numMoves);
await incrementProgress();
}
}
Our slowMove() function (in this case only moving the text frame back and forth 10 times) is run 100 times and the progress bar progresses (quickly) from 0% to 100%.
I am attaching the plugin for anyone who might want to see the whole code. Any suggestions for improvement welcomed.
progress-bar-2976637_ID.ccx (13.2 KB)
Philip