Skip to content

Instantly share code, notes, and snippets.

@pen
Last active April 18, 2017 09:24
Show Gist options
  • Save pen/61827963ee94932edc0173d979749ebf to your computer and use it in GitHub Desktop.
Save pen/61827963ee94932edc0173d979749ebf to your computer and use it in GitHub Desktop.
Google App Scriptで、時間がかかりすぎて打ち切られそうな処理を少しずつ処理してトリガーで再開するテンプレ
var LIMIT = 4; // 自主的に中断する時間(分)
var INTERVAL = 1; // 再開まで(分)
var CHUNK = 20; // 一気に処理するアイテム数。この数ごとに時間を気にする
var PROPERTY_NAME = "storage"; // データのセーブ用プロパティ名
function myFunction() {
// タイムリミットの時刻を計算
var time_limit = new Date() -0 + LIMIT * 60 * 1000;
var sheet = SpreadsheetApp.getActiveSheet();
// 前回保存したかもしれないデータをロード 最初のときはnull
var properties = PropertiesService.getScriptProperties();
var save = JSON.parse(properties.getProperty(PROPERTY_NAME))
|| { trigger: null, data: null };
// タイムリミットまでmain()を繰り返し呼ぶ
while (new Date() < time_limit) {
save.data = main(sheet, save.data);
if (!save.data) {
// 終わったので掃除しておしまい
replace_trigger(save.trigger, null);
properties.deleteProperty(PROPERTY_NAME);
return;
}
// break; /* ここでbreakするとmain()を呼ぶ毎に中断〜再開をテストできる */
}
// 次を予約
save.trigger = replace_trigger(
save.trigger,
arguments.callee.name,
INTERVAL * 60 * 1000
);
// セーブして終了
properties.setProperty(PROPERTY_NAME, JSON.stringify(save));
return;
}
// 再開用のトリガーの登録・更新・削除
function replace_trigger(old_trigger, func_name, interval) {
var triggers = ScriptApp.getProjectTriggers();
for (var i = 0; i < triggers.length; ++i) {
if (triggers[i].getUniqueId() == old_trigger) {
ScriptApp.deleteTrigger(triggers[i]);
break;
}
}
if (func_name) {
return ScriptApp.newTrigger(func_name)
.timeBased()
.after(interval)
.create()
.getUniqueId();
}
return;
}
// 本来の仕事はmain()に。キリのいいところで、
// まだ続くなら... return data;
// おわりなら... return;
// せよ。
// サンプル:連番をCHUNK個ずつ50まで書き込む
function main(sheet, data) {
if (!data) {
// 最初
sheet.clear();
data = { count: 0 };
}
if (data.count >= 50) {
// 終了
return;
}
// いくつか処理して…
for (i = 0; i < CHUNK; ++i) {
var row = data.count + i + 1;
sheet.getRange(row, 1, 1, 1)
.setValues([[ row ]]);
}
data.count += CHUNK;
// まだ続く
return data;
}
// サンプル:メールのスレッドを1000個とってくる
function main_sample_2(sheet, data) {
if (!data) {
sheet.clear();
data = { index: 0 };
}
var threads = GmailApp.search("label:all", data.index, CHUNK);
var rows = threads.map(function(t) {
// https://developers.google.com/apps-script/reference/gmail/gmail-thread
return [
t.getMessageCount(),
t.getLastMessageDate(),
t.getFirstMessageSubject()
];
});
if (rows.length == 0) {
return;
}
sheet.getRange(sheet.getLastRow() + 1, 1, rows.length, rows[0].length).setValues(rows);
data.index += CHUNK;
if (data.index >= 1000) {
return;
}
return data;
}
// サンプル:組織のメンバーを並べる
function main_sample_3(sheet, data) {
if (!data) {
sheet.clear();
data = { page_token: null };
}
// https://developers.google.com/apps-script/advanced/admin-sdk-directory
var page = AdminDirectory.Users.list({
domain: 'example.com',
orderBy: 'givenName',
maxResults: 100,
pageToken: data.page_token
});
var users = page.users;
if (!users) {
// 本当に1件もないときにここにくるらしい
return;
}
var rows = [];
for (var i = 0; i < users.length; i++) {
var user = users[i];
rows.push([ user.name.FullName, user.primaryEmail ]);
}
sheet.getRange(sheet.getLastRow() + 1, 1, rows.length, rows[0].length).setValues(rows);
data.page_token = page.nextPageToken;
if (!data.page_token) {
return;
}
return data;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment