Driver.js — это библиотека для создания интерактивных пошаговых руководств по интерфейсу веб-приложений. Одной из её мощных возможностей является поддержка асинхронных шагов, что позволяет динамически управлять последовательностью и содержимым шагов на основе данных или действий пользователя. Асинхронные шаги критически важны при работе с элементами, которые появляются или изменяются после выполнения JavaScript-кода или запросов к серверу.
Каждый шаг в Driver.js обычно задаётся объектом с полями
element, popover, description и
т.д. Для асинхронных шагов добавляется свойство onNext,
которое может быть асинхронной функцией:
const driver = new Driver();
driver.defineSteps([
{
element: '#button-load',
popover: {
title: 'Загрузка данных',
description: 'Нажмите кнопку, чтобы инициировать асинхронный процесс.',
position: 'bottom'
},
onNext: async () => {
await loadData();
console.log('Данные загружены, можно переходить к следующему шагу');
}
},
{
element: '#data-container',
popover: {
title: 'Контейнер данных',
description: 'Здесь отображаются загруженные данные',
position: 'top'
}
}
]);
driver.start();
В этом примере onNext использует асинхронную функцию
loadData, которая может совершать AJAX-запросы или любые
другие операции, возвращающие Promise. Driver.js ожидает
завершения этого промиса, прежде чем перейти к следующему шагу.
Иногда требуется не просто дождаться асинхронной функции, но и ввести
задержку перед переходом к следующему шагу. Для этого используется
await с функцией задержки:
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
driver.defineSteps([
{
element: '#button-process',
popover: {
title: 'Процессинг',
description: 'Дождитесь завершения процесса'
},
onNext: async () => {
await processData();
await delay(1000); // задержка 1 секунда перед следующим шагом
}
}
]);
Использование такой задержки помогает синхронизировать визуальные элементы интерфейса с прогрессом асинхронных операций.
В веб-приложениях часто элементы интерфейса создаются динамически, и
на момент инициализации шагов они могут отсутствовать в DOM. Driver.js
позволяет обрабатывать такие сценарии с помощью
async/await и проверки наличия элемента:
driver.defineSteps([
{
element: '#dynamic-element',
popover: {
title: 'Динамический элемент',
description: 'Элемент появляется после асинхронной загрузки'
},
onNext: async () => {
let el;
while (!el) {
el = document.querySelector('#dynamic-element');
if (!el) await new Promise(r => setTimeout(r, 200));
}
}
}
]);
Такой подход гарантирует, что Driver.js не попытается подсветить отсутствующий элемент, предотвращая ошибки и улучшая пользовательский опыт.
Иногда требуется, чтобы результат асинхронной операции влиял на
содержимое следующих шагов. Для этого onNext может
возвращать данные, которые затем можно использовать при конфигурации
следующих шагов:
driver.defineSteps([
{
element: '#fetch-btn',
popover: {
title: 'Получение данных',
description: 'Дождитесь ответа сервера'
},
onNext: async () => {
const response = await fetch('/api/user');
const data = await response.json();
return data.username;
}
},
{
element: '#user-display',
popover: {
title: 'Пользователь',
description: async () => {
const username = await driver.steps[0].onNext();
return `Привет, ${username}`;
}
}
}
]);
Использование асинхронной функции в description
позволяет динамически изменять текст поповера на основе полученных
данных.
Driver.js не ограничивает последовательность шагов только асинхронными действиями. Можно комбинировать синхронные и асинхронные шаги для создания гибкого руководства:
driver.defineSteps([
{
element: '#start',
popover: { title: 'Старт', description: 'Простой синхронный шаг' }
},
{
element: '#load-data',
popover: { title: 'Загрузка', description: 'Асинхронный шаг' },
onNext: async () => await fetchData()
},
{
element: '#finish',
popover: { title: 'Финиш', description: 'Возврат к синхронному шагу' }
}
]);
Такой подход позволяет создавать сложные интерактивные туры с ожиданием завершения операций, изменением DOM и обновлением интерфейса без нарушения логики последовательности.
await внутри onNext,
чтобы Driver.js корректно ожидал завершения операции.Асинхронные шаги в Driver.js обеспечивают полный контроль над динамическим интерфейсом, позволяя создавать туры, которые учитывают состояние приложения и данные, загружаемые в реальном времени.