Перейти к основному содержимому

Фоновый скрипт

Фоновые скрипты подходят для сценариев, которые должны работать постоянно. Это тип скрипта, специфичный для ScriptCat: они выполняются в песочнице и не имеют доступа к DOM. Их можно разрабатывать с теми же GM API, что и в Tampermonkey; замечания по совместимости отмечены в документации.

Фоновый скрипт (@background)

Фоновый скрипт объявляется атрибутом @background. Он позволяет скрипту продолжать работу в фоне после включения скрипта или запуска браузера.

Скрипт по расписанию (@crontab)

Скрипт по расписанию — разновидность фонового скрипта, подходящая для задач, которые нужно выполнять повторно по временному циклу.

Скрипт по расписанию объявляется атрибутом @crontab. Поддерживается минутное и секундное планирование, а также расширенный синтаксис ScriptCat once / once(...), чтобы не запускаться более одного раза в пределах одного временного цикла.

⚠️ Замечания:

  • В одном скрипте действует только первый @crontab
  • Рекомендуется, чтобы время одного выполнения + время повтора не превышало интервал cron, иначе запуски могут пересекаться

Замечания по cron-выражениям

Реализация cron в ScriptCat основана на node-cron с небольшим расширением стандартного синтаксиса cron.

Формат выражения

Стандартный формат из 5 полей (рекомендуется)

minute hour day month weekday

Расширенный формат из 6 полей (не рекомендуется)

second minute hour day month weekday

⚠️ Формат из 6 полей не рекомендуется В среде браузера нельзя гарантировать секундную точность, и это увеличивает накладные расходы — планирование фоновой страницы может задерживаться.

Синтаксис, доступный в каждом поле

СинтаксисЗначениеПример
*Любое значение* (каждую минуту/час)
числоКонкретное значение5 (5-я минута)
a,b,cНесколько дискретных значений1,15,30
a-bНепрерывный диапазон10-23
*/nКаждые n единиц*/5
a-b/nДиапазон с шагом10-50/10

Правила для дня недели

  • 1–6: понедельник — суббота
  • 0 или 7: воскресенье

Расширенный синтаксис once

Что означает once

Использование once в cron-выражении означает:

В текущем временном цикле допускается только одно успешное выполнение

Даже если более поздние моменты времени в том же цикле по-прежнему соответствуют правилу cron, скрипт снова не запустится.

once и once(...)

СинтаксисБазовое значение cron для этого поляОписание
once* (любое значение)Запуск при первом совпадении в цикле, без конкретного времени
once(expr)exprЗапуск только в моменты, соответствующие expr в цикле, и только один раз

once(expr) позволяет точно указать кандидаты по времени, сохраняя правило «не более одного раза за цикл». Внутри скобок поддерживается весь стандартный синтаксис cron (числа, диапазоны, шаги, списки).

Сравнение примеров:

* once * * * // any minute of every hour; runs on the first match, no further runs that hour
* once(9-17) * * * // between 9:00 and 17:59 every day, runs once per hour
0,30 once * * * // whichever of minute 0 or 30 is matched first each hour runs; no further runs that hour

Позиция once = ограничиваемый временной цикл

Где бы ни стояли once / once(...), это означает «запускать только один раз в пределах этой временной гранулярности».

Позиция onceПоведение
поле минутыНе более одного раза в минуту
поле часаНе более одного раза в час
поле дняНе более одного раза в день
поле месяцаНе более одного раза в месяц
поле дня неделиНе более одного раза в неделю

Примеры:

* once * * * // runs only once per hour
* * once * * // runs only once per day
* 9-18 once * * // runs only once between 9:00 and 18:59 each day

once в сочетании с диапазонами / списками / шагами

once / once(...) можно комбинировать с любым синтаксисом cron, но правило одно:

В пределах одного цикла после успешного запуска все дальнейшие совпадающие моменты времени игнорируются

Пример 1: диапазон

* 10 once * *

Смысл:

  • Каждый день кандидаты — 10:00–10:59
  • После первого совпадения за день
  • 10:05–10:59 больше не запускаются

Пример 2: список

* 1,3,5 once * *

Смысл:

  • Каждый день кандидаты — 1:00, 3:00 и 5:00
  • Если уже был запуск в 1:00
  • 3:00 и 5:00 будут пропущены

Пример 3: шаг

* */4 once * *

Смысл:

  • Каждый день кандидаты — 0:00, 4:00, 8:00, 12:00, 16:00 и 20:00
  • После первого запуска за день
  • Дальнейшие моменты времени не выполняются

Пример 4: once(...) с указанием кандидатов

* once(9-17) * * *

Смысл:

  • Каждый день кандидаты по часам — с 9:00 по 17:00
  • Цикл сбрасывается каждый час; в пределах часа первое совпадение останавливает дальнейшие запуски
  • Итог: один раз в час между 9:00 и 17:00 каждый день, всего 9 раз
* 9-18 once * *

Смысл:

  • Каждый день кандидаты — 9:00–18:59
  • once в поле дня фиксирует цикл «раз в день»
  • После первого совпадения за день до 18:59 больше ничего не выполняется

Примеры @crontab

Обычные

//@crontab * * * * * // runs once per minute
//@crontab * * * * * * // runs once per second (not recommended)
//@crontab 0 */6 * * * // runs on the hour every 6 hours
//@crontab 15 */6 * * * // runs at minute 15 every 6 hours
//@crontab * once * * * // runs at most once per hour
//@crontab * * once * * // runs at most once per day
//@crontab * 10 once * * // runs only once within the 10:00 hour each day (e.g. if it ran at 10:04, it won't run again from 10:05-10:59)
//@crontab * */4 once * * // checks at most once every 4 hours each day (e.g. if it ran at 4:00, it won't run again at 8, 12, 16, 20, 24, etc.)

Продвинутые

//@crontab * 1,3,5 once * * // runs once at 1:00, 3:00, or 5:00 each day (e.g. if it ran at 1:00, it won't run again at 3:00 or 5:00)
//@crontab * 10-23 once * * // runs once between 10:00 and 23:59 each day (e.g. if it ran at 10:04, it won't run again from 10:05-23:59)
//@crontab * once 13 * * // runs once per hour on the 13th of every month
//@crontab * once(9-17) * * * // runs once per hour between 9:00 and 17:00 each day
//@crontab 0,30 once * * * // whichever of minute 0 or 30 is matched first each hour runs; no repeat that hour
//@crontab * 9-18 once * * // runs only once between 9:00 and 18:00 each day

Рекомендации по использованию

Хорошо подходит для once

  • Задачи, которые нужно выполнить только один раз в день/час

  • Проверки статуса, синхронизация и отчётные скрипты

  • Избежание следующих проблем:

    • Браузер долго не открывали
    • Задержки планирования фоновой страницы
    • Повторное выполнение из‑за перезапуска браузера

Не рекомендуется для once

  • Задачи, которые должны сработать в точный момент
  • Скрипты, время выполнения которых может заметно превысить интервал cron
  • Задачи со строгими требованиями к числу выполнений

Проверка cron-выражений

При проверке cron-выражения временно замените once / once(...) на их базовое значение:

  • once*
  • once(expr)expr

Учтите, что тестовые инструменты могут не поддерживать расширенный формат из 6 полей.

Рекомендуемые инструменты:

На странице списка скриптов наведите курсор на столбец статуса выполнения, чтобы увидеть время следующего запланированного запуска.

Журналы

На странице списка скриптов при наведении на столбец статуса выполнения показывается подсказка со статусом; щелчок открывает содержимое журнала, выведенное через GM_log.

Отладка скрипта

Фоновые скрипты можно отлаживать прямо со страницы редактора, но с ограничениями:

  • value синхронизируется некорректно
  • меню registerMenu срабатывают некорректно

Чтобы отладить реальное окружение выполнения, включите режим разработчика в настройках расширения, затем откройте страницу расширения background.html для отладки.

Ошибки во время выполнения также можно посмотреть в журнале запусков.

image-20210903144155450

Promise

Настоятельно рекомендуется следующий шаблон — он также позволяет менеджеру скриптов отслеживать выполнение. Если скрипт выполняет любую асинхронную операцию, он должен вернуть Promise.

// ==UserScript==
// @name Background Script
// @namespace wyz
// @version 1.0.0
// @author wyz
// @background
// ==/UserScript==
return new Promise((resolve, reject) => {
if (Math.round((Math.random() * 10) % 2)) {
resolve("ok"); // succeeded
} else {
reject("error"); // failed, with the error reason
}
});
// ==UserScript==
// @name Scheduled script that runs once a day
// @namespace wyz
// @version 1.0.0
// @author wyz
// @crontab * * once * *
// ==/UserScript==
return new Promise((resolve, reject) => {
if (Math.round((Math.random() * 10) % 2)) {
resolve("ok"); // succeeded
} else {
reject("error"); // failed, with the error reason
}
});
// ==UserScript==
// @name Call an API
// @namespace wyz
// @version 1.0.0
// @author wyz
// @crontab * * once * *
// ==/UserScript==
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
url: "https://bbs.tampermonkey.net.cn/",
onload() {
resolve("ok"); // succeeded
},
onerror() {
reject("error"); // failed, with the error reason
},
});
});

Вызывайте resolve / reject только после того, как логика скрипта действительно завершена. После вызова менеджер считает выполнение скрипта завершённым, и последующие операции GM больше не действуют.

Повтор при ошибке

Фоновые скрипты ScriptCat поддерживают повтор при ошибке. При сбое скрипт может вызвать reject с CATRetryError, чтобы запустить повтор.

  • Минимальный интервал повтора: 5 секунд
  • Избегайте конфликта с собственным временем выполнения скрипта, иначе возможно дублирование
// ==UserScript==
// @name Retry example
// @namespace https://bbs.tampermonkey.net.cn/
// @version 0.1.0
// @description try to take over the world!
// @author You
// @crontab * * once * *
// @grant GM_notification
// ==/UserScript==

return new Promise((resolve, reject) => {
GM_notification({
title: "retry",
text: "Retrying in 10 seconds",
});
reject(new CATRetryError("xxx error", 10));
});