在Google表格Apps Script中实现带Promise的简易线程池
实现Google Apps Script原生简易线程池(带Promise支持)
首先得明确:Google Apps Script本身没有真正的多线程环境,但我们可以通过并发请求控制+Promise队列来模拟线程池的效果,实现你需要的「同时处理N个任务、获取返回值、等待全部完成」的需求。下面是一个完全基于原生JS的非压缩版实现,完美适配你的场景。
核心线程池实现
这个线程池类会维护一个任务队列,控制同时运行的任务数量,每个任务都是返回Promise的异步函数,完成后自动取下一个任务执行,最后可以获取所有任务的结果:
class ThreadPool { constructor(maxConcurrency = 5) { this.maxConcurrency = maxConcurrency; this.runningTasks = 0; this.taskQueue = []; this.results = []; this.resolveAll = null; // 初始化一个Promise,用于等待所有任务完成 this.allCompleted = new Promise(resolve => { this.resolveAll = resolve; }); } // 提交任务:传入一个返回Promise的函数 submit(taskFn) { return new Promise((resolve, reject) => { this.taskQueue.push({ taskFn, resolve, reject }); this._processQueue(); }); } // 处理任务队列 _processQueue() { // 如果还有空闲线程且队列有任务,就启动新任务 while (this.runningTasks < this.maxConcurrency && this.taskQueue.length > 0) { const { taskFn, resolve, reject } = this.taskQueue.shift(); this.runningTasks++; // 执行任务,处理结果 taskFn() .then(result => { this.results.push(result); resolve(result); }) .catch(error => { reject(error); }) .finally(() => { this.runningTasks--; this._processQueue(); // 任务完成后,继续处理下一个 // 检查是否所有任务都完成了 if (this.runningTasks === 0 && this.taskQueue.length === 0) { this.resolveAll(this.results); } }); } } // 等待所有任务完成,返回所有结果 waitForAll() { return this.allCompleted; } }
结合你的Google表格场景使用
接下来把你原来的逐行处理逻辑改成用这个线程池,包括处理多步依赖请求的情况:
function processSheetData() { const spreadsheet = SpreadsheetApp.getActiveSpreadsheet(); const sheet = spreadsheet.getActiveSheet(); const range = sheet.getDataRange(); const rows = range.getValues(); // 跳过表头(如果第一行是表头的话) const dataRows = rows.slice(1); // 创建线程池,设置并发数为5 const pool = new ThreadPool(5); // 遍历每一行,提交任务到线程池 dataRows.forEach((row, index) => { pool.submit(() => processSingleRow(row, index)); }); // 等待所有任务完成,然后汇总结果 pool.waitForAll().then(allResults => { // 这里处理所有任务的结果,比如写回表格 allResults.forEach((result, index) => { // 假设结果要写到第index+2行的某一列(因为跳过了表头) sheet.getRange(index + 2, 6).setValue(result); }); SpreadsheetApp.getUi().alert('所有数据处理完成!'); }).catch(error => { console.error('处理出错:', error); SpreadsheetApp.getUi().alert(`处理失败:${error.message}`); }); } // 处理单行数据的函数(包含多步依赖请求) function processSingleRow(row, rowIndex) { return new Promise((resolve, reject) => { try { // 第一步:构建payload并发送第一个请求 const payload1 = { // 从row中提取数据,比如row[0], row[1]等 id: row[0], name: row[1] }; const options1 = { method: "POST", payload: payload1 }; UrlFetchApp.fetch("http://your-first-api-url", options1) .then(response1 => { const data1 = JSON.parse(response1.getContentText()); // 第二步:依赖第一个请求的结果,发送第二个请求 const payload2 = { parentId: data1.id, detail: row[2] }; const options2 = { method: "POST", payload: payload2 }; return UrlFetchApp.fetch("http://your-second-api-url", options2); }) .then(response2 => { const data2 = JSON.parse(response2.getContentText()); // 继续后续的3-4个依赖请求... // ...省略中间步骤... // 最后返回该行的处理结果 resolve(`行${rowIndex+2}处理完成:${data2.result}`); }) .catch(error => { reject(new Error(`行${rowIndex+2}处理失败:${error.message}`)); }); } catch (error) { reject(new Error(`行${rowIndex+2}初始化失败:${error.message}`)); } }); }
关键说明
- 并发控制:线程池通过
maxConcurrency限制同时运行的任务数,避免一次性发送过多请求导致被API限流,或者触发Apps Script的请求配额限制。 - 任务结果追踪:每个
submit调用返回的Promise可以单独获取该行的结果,waitForAll()则会等待所有任务完成后返回所有结果的数组。 - 错误处理:单个任务失败不会影响其他任务,你可以在
waitForAll()的catch里统一处理全局错误,也可以在单个任务的Promise里做针对性错误处理。 - 适配Apps Script特性:这里用的是原生Promise和
UrlFetchApp,完全符合Apps Script的运行环境,不需要任何外部库。
内容的提问来源于stack exchange,提问作者Deviling Master
相关产品推荐
相关产品推荐

