As @azawaza points out, you should be using a Lock with an appropriate scope, and a Script Lock is the better fit for your scenario. This is discussed in Does this code lock the onFormSubmit(e) method correctly?
If the critical section of code is sufficiently quick, then there's no real concern about making the user updating Document 2 wait while another update to Document 1 proceeds; they won't wait long. Something like:
function doGet1(e){
// Perform any "pre" operations on private
// or non-critical shared resources.
var params=e.parameters;
// Get a script lock, because we're about to modify a shared resource.
var lock = LockService.getScriptLock();
// Wait for up to 10 seconds for other processes to finish.
lock.waitLock(10000);
////// Critical section begins vvvvv
var doc = DocumentApp.openById(params['docId']);
// change text of the document here
doc.saveAndClose();
////// Critical section ends ^^^^^
lock.releaseLock();
// Continue with operations on private
// or non-critical shared resources.
return ContentService.createTextOutput("Document updated.")
}
Specific resource locks
Out of the box, the Google Apps Script Lock Service is designed to protect Critical Sections of code. If we want to control access to a specific resource (perhaps for a long-ish time), such as a Google Document, we can adapt it by changing what we are "locking".
In this example, the Lock service protects a critical section wherein Script Properties are checked and updated. These properties have "keys" that match our docId
parameter; the value is not important, as we can use simple existence of the key as our test.
Note: Currently, this script could block a user "forever" (until script times out) if another script fails to delete the property protecting their use of the shared document. You'd want to take greater care in production code.
function doGet2(e){
// Perform any "pre" operations on private
// or non-critical shared resources.
var params=e.parameters;
// Wait for exclusive access to docId
var ready = false;
// Get a script lock, because we're about to modify a shared resource.
var lock = LockService.getScriptLock();
while (!ready) {
// Wait for up to 1 second for other processes to finish.
if (lock.tryLock(1000)) {
////// Critical section begins vvvvv
var properties = PropertiesService.getScriptProperties();
// If nobody has "locked" this document, lock it; we're ready.
if (properties.getProperty(docId) == null) {
// Set a property with key=docId.
properties.setProperty(docId,"Locked");
ready = true;
}
////// Critical section ends ^^^^^
lock.releaseLock();
}
}
// We have exclusive access to docId now.
var doc = DocumentApp.openById(params['docId']);
// change text of the document here
doc.saveAndClose();
// Delete the "key" for this document, so others can access it.
properties.deleteProperty(docId);
return ContentService.createTextOutput("Document updated.")
}
Named Locks
The logic that we've used in the previous example can be encapsulated into an Object to provide a more elegant interface. In fact, Bruce McPherson has done just that with his cNamedLock Library, described on his Desktop Liberation site. Using that library, you can implement document-specific locking like this:
function doGet3(e){
// Perform any "pre" operations on private
// or non-critical shared resources.
var params=e.parameters;
// Get a named lock.
var namedLock = new NamedLock().setKey(docId);
namedLock.lock();
////// Critical section begins vvvvv
// We have exclusive access to docId now.
var doc = DocumentApp.openById(params['docId']);
// change text of the document here
doc.saveAndClose();
////// Critical section ends ^^^^^
namedLock.unlock();
return ContentService.createTextOutput("Document updated.")
}