Reputation: 121
It seems that if one uses SlidesApp.getActivePresentation()
in AppsScript, the result of the function is not fresh but rather something that was already prepared beforehand.
Scenario
Imagine you have two users performing the following function in AppsScript simultaneously:
function updateSlideText(slideId) {
// Request exclusive write access to the document
var lock = LockService.getDocumentLock();
lock.waitLock(15000);
// Perform changes
var presentation = SlidesApp.getActivePresentation();
var textBox = presentation.getSlideById(MY_SLIDE_ID).getPageElementById(MY_TEXTBOX_ID);
textBox.asShape().getText().setText('My text');
// Save and release lock
presentation.saveAndClose();
lock.releaseLock();
}
If this function is called twice at the same time, the resulting slide contains text "My textMy text".
When I add Utilities.sleep(10000)
just before the lock release, it delays the 2nd execution by 10s but after those 10s I still end up with the same result. On the other hand, if I actually delay calling the function 10s, the output is fine.
From this I conclude that it does not matter if I call saveAndClose
and use locks. Once the function is called, it will always have stale data. Is there a way around this? Is it not possible to request that fresh data will be loaded after the lock is aquired?
More details
Some more pseudo-code to better illustrate the problem use-case:
// The addon frontend
websocket.onMessage((message) => {
if (message.type === 'pollUpdate') {
const slideWithPoll = store.getState().slides.find(
slide => slide.pollId === message.pollId
);
if (slideWithPoll.title !== message.poll.title) {
google.script.run.updateSlideText(slideWithPoll.id, message.poll.title);
}
}
});
Upvotes: 3
Views: 239
Reputation: 201603
I believe your goal as follows.
For this, how about this answer?
When I tested your situation, I could confirm the same issue like My textMy text
. When I tested several times, in this case, I thought that the LockService might not affect to Google Slides. So as a workaround, I would like to propose to use Web Apps as the wrapper. Because it has already been known that Web Apps can run exclusively by the LockService. The flow of this workaround is as follows.
By this, even when the script is run, simultaneously, the script can be exclusively run with the LockService.
The usage of this sample script is as follows. Please do the following flow.
When your script is used, it becomes as follows. Please copy and paste the following script to the script editor. Please set MY_SLIDE_ID
and MY_TEXTBOX_ID
.
function doGet() {
// This is your script.
var presentation = SlidesApp.getActivePresentation();
var textBox = presentation.getSlideById(MY_SLIDE_ID).getPageElementById(MY_TEXTBOX_ID);
var text = textBox.asShape().getText();
text.setText('My text');
return ContentService.createTextOutput("ok");
}
// Please run this function.
function main() {
var lock = LockService.getDocumentLock();
if (lock.tryLock(10000)) {
try {
const url = "https://script.google.com/macros/s/###/exec"; // Please set the URL of Web Apps after you set the Web Apps.
const res = UrlFetchApp.fetch(url);
console.log(res.getContentText())
} catch(e) {
throw new Error(e);
} finally {
lock.releaseLock();
}
}
}
https://www.googleapis.com/auth/drive.readonly
and https://www.googleapis.com/auth/drive
to the access token. These scopes are required to access to Web Apps.Copy the URL of Web Apps. It's like https://script.google.com/macros/s/###/exec
.
Please set the URL of https://script.google.com/macros/s/###/exec
to url
of above script. And please redeploy Web Apps. By this, the latest script is reflected to the Web Apps. So please be careful this.
Please run the function of main()
by 2 users, simultaneously as you have tested. By this, it is found that the script is run exclusively. In my environment, in this case, I confirmed that even when the LockService is not used, the script is exclusively run. But I would like to recommend to use the LockService just in case.
Upvotes: 2