Apps Script:如何实现同类型命名锁以优化多函数锁机制
自定义命名锁实现与第三方库特性验证
场景与需求
现有环境
- 表格:
sheetA、sheetB - 表单:
formA(数据可写入两个表格)、formB(仅写入sheetB) - 处理函数:
functionAB(写入sheetA+sheetB)、functionB(仅写入sheetB)
预期行为
- 重复直接调用
functionAB,仅首次执行有效 functionAB内部调用functionB时,不触发functionB的锁定逻辑- 重复直接调用
functionB,仅首次执行有效
当前实现的局限
使用Google Apps Script的LockService提供的scriptLock和userLock实现了需求,但存在明显问题:
- 仅支持
script、user、doc三种固定锁类型,扩展性不足 - 必须严格对应锁的类型与使用位置,维护时容易出错
核心诉求
能否实现自定义命名锁(如lock_ab、lock_b),且满足:函数执行出错时锁自动释放(全局变量/属性变量无法实现此特性)
另外找到一款第三方命名锁库,但文档未提及是否支持异常自动释放,寻求实际使用者确认该特性。
现有实现代码
// functionAB 代码片段 const lockScript = LockService.getScriptLock() lockScript.tryLock(0) // 尝试锁定以验证调用有效性 if (!lockScript.hasLock()) { // 重复调用处理逻辑 return } // 执行写入sheetA的逻辑 functionB(partOfFormAdataForSheetB) // functionB 完整实现 function functionB(dataForSheetB) { // 可直接由formB调用,也可作为functionAB的辅助函数 const lockUser = LockService.getUserLock() lockUser.tryLock(0) if (!lockUser.hasLock()) { // 重复调用处理逻辑 return } // 执行写入sheetB的逻辑 }
锁逻辑补充
锁本质是需要主动检查的标记,与全局变量的核心区别:函数停止执行时锁会自动释放
解决方案
1. 自行封装自定义命名锁
基于LockService和PropertiesService可实现带自动释放的命名锁,核心是用try...finally块保证异常场景下锁能被释放,代码示例:
class NamedLock { constructor(lockName) { this.lockName = lockName; // 用文档锁保证属性操作的原子性 this.baseLock = LockService.getDocumentLock(); this.props = PropertiesService.getDocumentProperties(); } // 尝试获取锁,timeoutMs为等待超时时间,0表示立即返回结果 tryLock(timeoutMs = 0) { if (!this.baseLock.tryLock(timeoutMs)) return false; try { const isLocked = this.props.getProperty(this.lockName) === 'true'; if (isLocked) return false; this.props.setProperty(this.lockName, 'true'); return true; } finally { this.baseLock.releaseLock(); } } // 主动释放锁 releaseLock() { if (this.baseLock.hasLock()) { this.props.deleteProperty(this.lockName); this.baseLock.releaseLock(); } else { // 先获取基础锁再修改属性 if (this.baseLock.tryLock(0)) { this.props.deleteProperty(this.lockName); this.baseLock.releaseLock(); } } } // 检查锁是否被持有 hasLock() { this.baseLock.tryLock(0); try { return this.props.getProperty(this.lockName) === 'true'; } finally { this.baseLock.releaseLock(); } } } // 改造后的业务函数 function functionAB() { const abLock = new NamedLock('lock_function_ab'); if (!abLock.tryLock(0)) { // 重复调用处理逻辑,比如直接终止或返回提示 return; } try { // 执行写入sheetA的逻辑 // 链式调用functionB,传入标记跳过锁逻辑 functionB(partOfFormAdataForSheetB, true); } finally { // 无论正常执行还是报错,都释放锁 abLock.releaseLock(); } } function functionB(data, isChainedCall = false) { // 链式调用时直接执行逻辑,不触发锁 if (isChainedCall) { // 执行写入sheetB的逻辑 return; } // 直接调用时触发锁逻辑 const bLock = new NamedLock('lock_function_b'); if (!bLock.tryLock(0)) { // 重复调用处理逻辑 return; } try { // 执行写入sheetB的逻辑 } finally { bLock.releaseLock(); } }
说明:
try...finally块确保异常场景下锁一定会被释放,满足自动释放要求- 通过参数
isChainedCall区分链式调用与直接调用,避免内部调用触发锁定
2. 第三方命名锁库的特性验证
如果使用第三方命名锁库,只要它底层依赖Google Apps Script的LockService,或遵循GAS运行时的资源自动回收机制,那么函数出错时锁应该会自动释放。可通过以下步骤自行验证:
- 在持有锁的函数内主动抛出异常
- 调用锁的
hasLock()方法检查状态 - 再次调用该函数,验证能否正常获取锁并执行逻辑
内容的提问来源于stack exchange,提问作者Dmytrq
相关产品推荐
相关产品推荐

