Покупатель нажимает «Оформить заказ» — и ждёт. А в это время сайт успевает сходить в CRM, отправить письмо, дёрнуть службу доставки и записать что-нибудь в аналитику. Каждый такой вызов добавляет секунды к оформлению, а если внешний сервис не отвечает, покупатель может увидеть ошибку по причине, к его заказу отношения не имеющей.
Решение простое по идее: тяжёлую работу нужно выполнять после того, как покупатель увидел «Спасибо за заказ». Ниже — два способа это сделать: быстрый, на одну строку, и правильный, через очередь задач, которая в WooCommerce уже есть.
Быстрый приём: неблокирующий запрос
Функция wp_remote_post() из WordPress Core API умеет не дожидаться ответа. Достаточно передать параметр 'blocking' => false — PHP отправит запрос и сразу пойдёт дальше, не тратя время на ожидание.
$data = [
'order_id' => $order_id,
'safe_route_id' => $safe_route_id,
'deal_id' => $deal_id,
];
$response = wp_remote_post( 'https://crm.example.com/ajax-orders.php', [
'method' => 'POST',
'timeout' => 45,
'redirection' => 5,
'httpversion' => '1.0',
'blocking' => false, // Асинхронный запрос!
'headers' => [],
'body' => $data, // Передаём массив, wp_remote_post сам закодирует
'cookies' => [],
] );
// Функция-отправитель НЕ ждёт ответа.
// $response будет содержать объект WP_Error или пустой массив.
Когда этого достаточно и чем приём плох
Способ хорош своей простотой: одна строка в существующем коде — и оформление заказа перестало тормозить. Он уместен, когда результат вам безразличен: пинг в аналитику, необязательное уведомление, «выстрелил и забыл».
Но у него есть цена, и она серьёзная. Вы не узнаете, дошёл ли запрос: ответ не читается, ошибки не видно. Если внешний сервис лежал в этот момент — данные просто потеряны, и никто об этом не сообщит. Нет ни повторной попытки, ни журнала, ни возможности посмотреть, что происходило вчера. Для пинга это приемлемо, для передачи заказа в CRM — нет.
Action Scheduler: очередь задач, встроенная в WooCommerce
Action Scheduler — это библиотека фоновых задач, которая уже установлена вместе с WooCommerce. Отдельно ставить ничего не нужно: если магазин работает, очередь доступна. WooCommerce использует её и для собственных нужд — отложенных операций с заказами, подписками, обновлениями данных.
От wp_cron её отличает то, что задачи хранятся в собственных таблицах базы данных, а не в одной разросшейся опции, и каждая задача имеет статус, аргументы, группу и журнал выполнения. То есть вы всегда видите, что было запланировано, что выполнено, а что упало.
Поставить задачу в очередь
Базовый сценарий — «сделай это как можно скорее, но не сейчас». За него отвечает as_enqueue_async_action():
// 1. Ставим задачу в очередь в момент создания заказа
add_action( 'woocommerce_checkout_order_created', function ( $order ) {
as_enqueue_async_action(
'myshop_notify_crm', // хук, который выполнится в фоне
[ 'order_id' => $order->get_id() ], // аргументы
'myshop' // группа — для фильтрации в админке
);
} );
// 2. Описываем, что именно делать в фоне
add_action( 'myshop_notify_crm', function ( $order_id ) {
$order = wc_get_order( $order_id );
if ( ! $order ) {
return;
}
$response = wp_remote_post( 'https://crm.example.com/api/orders', [
'timeout' => 30,
'body' => [
'order_id' => $order->get_id(),
'total' => $order->get_total(),
'email' => $order->get_billing_email(),
],
] );
// Здесь ответ уже можно проверить — мы никого не задерживаем
if ( is_wp_error( $response ) ) {
throw new Exception( 'CRM недоступна: ' . $response->get_error_message() );
}
} );
Обратите внимание на две детали. Аргументы передаются в обработчик по порядку значений массива, а не как единый массив. И внутри обработчика уже можно спокойно проверять ответ и бросать исключение: покупатель давно ушёл со страницы, а упавшая задача получит статус «failed» и останется в журнале — вы её увидите.
Отложить на будущее
Если задачу нужно выполнить не сразу, а через какое-то время — например, проверить оплату через пятнадцать минут:
as_schedule_single_action(
time() + 15 * MINUTE_IN_SECONDS,
'myshop_check_payment',
[ 'order_id' => $order_id ],
'myshop'
);
Повторяющиеся задачи
Для регулярной синхронизации остатков подойдёт as_schedule_recurring_action(). Важно не поставить её дважды — иначе получите две параллельные серии задач:
add_action( 'init', function () {
if ( ! as_has_scheduled_action( 'myshop_sync_stock', [], 'myshop' ) ) {
as_schedule_recurring_action(
time(),
HOUR_IN_SECONDS,
'myshop_sync_stock',
[],
'myshop'
);
}
}, 20 );
У всех функций планирования есть параметр $unique — если передать true, повторная постановка той же задачи будет отклонена. Это второй способ защититься от дублей, более надёжный, чем ручная проверка.
Проверить и отменить
Полезный минимум для управления очередью:
// Есть ли такая задача? (быстрая проверка)
as_has_scheduled_action( 'myshop_check_payment', [ 'order_id' => 42 ], 'myshop' );
// Когда выполнится ближайшая
as_next_scheduled_action( 'myshop_check_payment', [ 'order_id' => 42 ], 'myshop' );
// Отменить ближайшую
as_unschedule_action( 'myshop_check_payment', [ 'order_id' => 42 ], 'myshop' );
// Отменить все такие задачи
as_unschedule_all_actions( 'myshop_check_payment', [], 'myshop' );
Одно правило по времени вызова: функции Action Scheduler нельзя дёргать слишком рано. Библиотека готова к работе после хука init, поэтому планируйте задачи внутри init с невысоким приоритетом или позже — как в примере выше.
Где смотреть, что происходит
Главное преимущество очереди перед «выстрелил и забыл» — прозрачность. В админке откройте WooCommerce → Status → Scheduled Actions. Там видны все задачи с фильтром по статусу: ожидающие, выполняющиеся, завершённые, упавшие и отменённые. У каждой можно раскрыть журнал и увидеть, когда она создана, когда начата и с какой ошибкой завершилась.
Это тот случай, когда пять минут на настройку группы ('myshop' в примерах) окупаются: в магазине с активным WooCommerce задач тысячи, и без группы своя задача теряется среди чужих.
Как очередь выполняется на самом деле
Тут скрыт нюанс, из-за которого чаще всего и возникают вопросы «почему задачи не выполняются». Action Scheduler запускается через WP-Cron, а WP-Cron в WordPress зависит от посещаемости: он срабатывает при заходе на сайт. На магазине с трафиком это незаметно, но на тестовом или малопосещаемом сайте задачи будут копиться и выполняться рывками.
Лечится это переводом на системный cron: отключаем встроенный планировщик в wp-config.php и вызываем его по расписанию сервера.
// wp-config.php
define( 'DISABLE_WP_CRON', true );
# crontab, раз в минуту
* * * * * curl -s https://example.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1
По документации WooCommerce задачи обрабатываются пачками по 20 штук, и одновременно может выполняться до пяти очередей — это защищает PHP от исчерпания памяти на больших объёмах. Если задач накопились десятки тысяч, разбирать их очередь будет постепенно, а не мгновенно.
Типичные ошибки
Передавать в задачу объекты вместо идентификаторов. Аргументы сохраняются в базу, поэтому кладите туда order_id, а не объект заказа целиком: объект к моменту выполнения устареет, а таблица распухнет.
Рассчитывать на автоматический повтор. Упавшая задача помечается как failed и остаётся лежать — сама она не перезапустится. Если повтор нужен, планируйте его явно из обработчика ошибки.
Забыть про дубли. Постановка повторяющейся задачи при каждой загрузке страницы — классическая причина, по которой через неделю очередь забита тысячами одинаковых записей. Используйте $unique или предварительную проверку.
Не следить за размером таблиц. Завершённые задачи хранятся некоторое время, а затем очищаются автоматически, но на активном магазине таблицы Action Scheduler всё равно становятся заметными. Это стоит учитывать при работе над производительностью — подробнее об этом мы писали в чек-листе оптимизации WordPress.
Что выбрать
Неблокирующий wp_remote_post() — это микрооптимизация для необязательных вызовов, где потеря запроса ничего не стоит. Одна строка, никаких зависимостей, никакого контроля.
Action Scheduler — рабочий инструмент для всего, что действительно должно произойти: передача заказа в CRM, отправка документов, синхронизация остатков, обращения к платёжным сервисам. Он уже стоит в вашем магазине, даёт журнал, отмену, группировку и понятную картину в админке. Если сомневаетесь — берите его: разница в объёме кода составляет несколько строк, а разница в надёжности принципиальная.
Кстати, следить за развитием самой платформы тоже полезно: в разборе WooCommerce 11.0 мы смотрели, что изменилось в последних версиях магазина.
Частые вопросы
Нет, если у вас работает WooCommerce — библиотека поставляется вместе с ним и уже доступна. Отдельная установка нужна только для сайтов на WordPress без WooCommerce.
Неблокирующий запрос не даёт никаких гарантий: ответ не читается, ошибка не видна, журнала нет. Action Scheduler хранит задачи в базе, показывает статусы и журнал в админке, позволяет отменять и группировать задачи. Для всего важного нужна очередь.
Чаще всего дело в WP-Cron: он срабатывает при заходах на сайт, поэтому на малопосещаемом или тестовом сайте очередь двигается рывками. Решение — отключить встроенный планировщик через DISABLE_WP_CRON и вызывать wp-cron.php системным cron.
Передавайте параметр $unique = true при планировании либо проверяйте наличие задачи через as_has_scheduled_action() перед добавлением. Особенно важно для повторяющихся задач, иначе они накапливаются при каждой загрузке страницы.
Источники
- 1.Action Scheduler — API Referencehttps://actionscheduler.org/api/
- 2.woocommerce/action-scheduler — docs/api.mdhttps://github.com/woocommerce/action-scheduler/blob/trunk/docs/api.md
- 3.Scheduled Actions — документация WooCommercehttps://woocommerce.com/document/understanding-the-woocommerce-system-status-report/scheduled-actions/


