View Categories

События

Существует два основных подхода к организации логики программы: опрос в суперцикле (superloop polling) и событийно-ориентированный подход (event-based, event-driven).

Polling #

Опрос - это когда мы вручную опрашиваем в суперцикле какие-то функции или методы объектов и ожидаем от них результат. Например - опрос кнопки или доступность Serial для чтения:

void loop() {
    if (buttonClick()) {
        // обработка клика по кнопке
    }

    if (Serial.available()) {
        // чтение данных
    }
}

Это самый простой вариант, у него очень компактная реализация - нет лишнего кода и лишних действий в программе. В то же время он визуально занимает много места - если писать всю программу в loop таким образом - она превратится в нечитаемое полотно из опросов и реакций на их результат.

Функция-обработчик #

Одно из решений - вынести всю логику обработки в отдельную функцию, которую потом можно будет перенести в отдельный файл для удобства:

void processClick() {
    // обработка клика по кнопке
}

void loop() {
    if (buttonClick()) processClick();
}

Получилась функция для обработки события (event) - клика по кнопке, функция будет вызвана, когда произойдёт событие. Такая функция называется обработчиком.

Callback #

Здесь мы всё ещё вручную опрашиваем кнопку и вручную вызываем обработчик, но во многих библиотеках предусмотрено подключение обработчика: можно указать, какую функцию нужно вызвать при наступлении события. Такая функция-обработчик, переданная объекту, называется callback - объект запоминает её к себе в память и вызывает при наступлении события.

Существует также термин event listener - это тоже функция-обработчик события, но событие в данном случае рассылается всем, кто его ожидает. Грубо говоря, callback - это когда начальник попросил подчинённого лично сообщить ему о выполненной работе. А event listener - когда работник сделал работу и написал об этом публично, и все кому это интересно - прочитают данную информацию, "подпишутся на рассылку"

Это может выглядеть примерно так (используется выдуманный класс кнопки, но мы напишем его позже в другом уроке):

// создание кнопки
Button btn;

void processClick() {
    // обработка клика по кнопке
}

void setup() {
    // подключение обработчика
    btn.onClick(processClick);
}

void loop() {
    // опрос кнопки, может отсутствовать
    btn.tick();
}

Тикер #

В примере выше btn.tick() - некий системный метод, который занимается обработкой кнопки, он должен вызываться в суперцикле как можно чаще - такие штуки часто называют тикерами. Это неблокирующая функция, которая занимается обработкой данных, переключением таймеров и машин состояний внутри себя и обеспечивает работу объекта. Тикера может и не быть - если опрос кнопки реализован на прерываниях (о них - в следующих уроках). Во многих библиотеках используется подобный механизм "тикер + обработчики" - в суперцикл помещается тикер, подключаются обработчики и оно работает.

Это удобно - позволяет разгрузить суперцикл и легко структурировать программу, сделать её более читаемой. Но есть и недостатки - на вызов функции тратится дополнительное время, а также нужно хранить указатель на обработчик в оперативной памяти

Некоторые языки, например JavaScript, изначально событийно-ориентированные - в нём нет суперцикла и не нужно его делать: всё работает на событиях и обработчиках. Но "под капотом" цикл всё же имеется - в нём точно так же проверяются события и вызываются подключенные обработчики, просто это полностью скрыто от программиста.

0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
Прокрутить вверх