Spec-Zone.ru › Web APIs

Проверка сроков выполнения

В этой статье мы рассмотрим сложный пример, связанный с проверкой текущего времени и даты по отношению к сроку, хранящемуся в IndexedDB. Основная сложность здесь заключается в сравнении сохранённых данных о сроке (месяц, час, день и т. д.) с текущим временем и датой, взятыми из объекта Date.

A screenshot of the sample app. A red main title saying To do app, a test to-do item, and a red form for users to enter new tasks

Основным примером приложения, на которое мы будем ссылаться в этой статье, является Уведомления о задачах, простое приложение списка задач, которое хранит заголовки задач и сроки выполнения с помощью IndexedDB, а затем предоставляет пользователям уведомления, когда сроки истекают, с помощью API Notification и Vibration. Вы можете скачать приложение Уведомления о задачах с GitHub и поработать с исходным кодом или посмотреть приложение в работе.

Основная проблема

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

Это было бы легко, если бы мы просто сравнивали два Date объекта, но, конечно, люди не хотят вводить информацию о сроках в том же формате, что и JavaScript. Человекочитаемые даты сильно отличаются, имея множество различных представлений.

Запись информации о дате

Чтобы обеспечить приемлемый пользовательский опыт на мобильных устройствах и уменьшить неоднозначность, я решил создать форму HTML с:

The form of the to-do app, containing fields to fill in a task title, and minute, hour, day, month and year values for the deadline.

  • Поле ввода текста для ввода заголовка для вашего списка задач. Это наименее неизбежная часть ввода пользователем.
  • Поля ввода числа для часов и минут срока выполнения. В браузерах, поддерживающих type="number", вы получаете удобный числовой выбор с помощью стрелок вверх и вниз. На мобильных платформах, как правило, используется цифровая клавиатура для ввода данных, что удобно. В других случаях вы просто получаете стандартное текстовое поле, что тоже нормально.
  • <select> элементы для ввода дня, месяца и года срока выполнения. Поскольку эти значения являются наиболее неоднозначными для ввода пользователем (7, воскресенье, вс? 04, 4, апрель, апр? 2013, '13, 13?), я решил, что лучшим решением будет дать им возможность выбора, что также экономит раздражающий ввод данных на мобильных устройствах. Дни записываются как цифровые дни месяца, месяцы — как полные названия месяцев, а годы — как полные четырёхзначные числа.

При нажатии кнопки отправки формы мы запускаем функцию addData(), которая начинается так:

function addData(e) {
  e.preventDefault();

  if (!title.value || !hours.value || !minutes.value || !day.value || !month.value || !year.value) {
    note.appendChild(document.createElement("li")).textContent = "Data not submitted — form incomplete.";
    return;
  }

В этом фрагменте мы проверяем, заполнены ли все поля формы. Если нет, мы выводим сообщение в область уведомлений разработчика (см. левый нижний угол пользовательского интерфейса приложения), чтобы сообщить пользователю о происходящем, и выходим из функции. Этот шаг предназначен в основном для браузеров, которые не поддерживают проверку форм HTML (я использовал атрибут required в своём HTML для принудительной проверки в тех, которые её поддерживают).

   else {
    const newItem = [
      {
        taskTitle: title.value,
        hours    : hours.value,
        minutes  : minutes.value,
        day      : day.value,
        month    : month.value,
        year     : year.value,
        notified : "no"
      }
    ];

    // open a read/write db transaction, ready for adding the data
    const transaction = db.transaction(["toDoList"], "readwrite");

    // report on the success of opening the transaction
    transaction.oncomplete = (event) => {
      note.appendChild(document.createElement("li")).textContent =
        "Transaction opened for task addition.";
    };

    transaction.onerror = (event) => {
      note.appendChild(document.createElement("li")).textContent =
        "Transaction not opened due to error. Duplicate items not allowed.";
    };

    // create an object store on the transaction
    const objectStore = transaction.objectStore("toDoList");

    // add our newItem object to the object store
    const request = objectStore.add(newItem[0]);

В этом разделе мы создаём объект, называемый newItem, который хранит данные в формате, необходимом для вставки в базу данных. Следующие несколько строк открывают транзакцию базы данных и предоставляют сообщения, чтобы уведомить пользователя, если это удалось или нет. Затем создаётся objectStore, в который добавляется новый элемент. Свойство notified объекта данных указывает, что срок выполнения элемента списка задач ещё не наступил и не был уведомлён — подробнее об этом позже!

Примечание: Переменная db хранит ссылку на экземпляр базы данных IndexedDB; затем мы можем использовать различные свойства этой переменной для управления данными.

    request.onsuccess = (event) => {

      note.appendChild(document.createElement("li")).textContent = "New item added to database.";

      title.value = "";
      hours.value = null;
      minutes.value = null;
      day.value = "01";
      month.value = "January";
      year.value = 2020;
    };
  }

В этом следующем разделе создаётся сообщение в журнал, указывающее на успешное добавление нового элемента, и сбрасывается форма, чтобы она была готова для ввода следующей задачи.

  // update the display of data to show the newly added item, by running displayData() again.
  displayData();
};

В заключение мы запускаем функцию displayData(), которая обновляет отображение данных в приложении, чтобы показать новую задачу, которая только что была введена.

Проверка, наступил ли срок выполнения

На этом этапе наши данные находятся в базе данных; теперь мы хотим проверить, наступил ли срок выполнения для каких-либо из них. Это выполняется нашей функцией checkDeadlines():

function checkDeadlines() {
  const now = new Date();

Сначала мы получаем текущую дату и время, создав пустой объект Date. Легко, да? Это только что станет немного сложнее.

const minuteCheck = now.getMinutes();
const hourCheck = now.getHours();
const dayCheck = now.getDate();
const monthCheck = now.getMonth();
const yearCheck = now.getFullYear();

Объект Date имеет ряд методов для извлечения различных частей даты и времени внутри него. Здесь мы получаем текущие минуты (дают простое числовое значение), часы (дают простое числовое значение), день месяца (getDate() необходим для этого, так как getDay() возвращает день недели, 1-7), месяц (возвращает число от 0 до 11, см. ниже) и год (getFullYear() необходим; getYear() устарел и возвращает странное значение, которое мало кому пригодится!).

  const objectStore = db.transaction(['toDoList'], "readwrite").objectStore('toDoList');

  objectStore.openCursor().onsuccess = (event) => {
    const cursor = event.target.result;
    let monthNumber;

    if (cursor) {

Далее мы создаём ещё одну транзакцию IndexedDB objectStore, и используем метод openCursor() для открытия курсора, который, по сути, является способом в IndexedDB итерироваться по всем элементам в хранилище. Затем мы итерируемся по всем элементам в курсоре до тех пор, пока в курсоре есть действительный элемент.

switch (cursor.value.month) {
  case "January":
    monthNumber = 0;
    break;
  case "February":
    monthNumber = 1;
    break;

  // other lines removed from listing for brevity

  case "December":
    monthNumber = 11;
    break;
  default:
    alert("Incorrect month entered in database.");
}

Первое, что мы делаем, это конвертируем названия месяцев, которые мы сохранили в базе данных, в число месяца, которое поймёт JavaScript. Как мы видели раньше, объект JavaScript Date создаёт значения месяцев в виде числа от 0 до 11.

if (
  Number(cursor.value.hours) === hourCheck &&
  Number(cursor.value.minutes) === minuteCheck &&
  Number(cursor.value.day) === dayCheck &&
  monthNumber === monthCheck &&
  cursor.value.year === yearCheck &&
  notified === "no"
) {
  // If the numbers all do match, run the createNotification()
  // function to create a system notification
  createNotification(cursor.value.taskTitle);
}

С текущими частями времени и даты, которые мы хотим проверить по отношению к сохранённым значениям в IndexedDB, всё собрано, настало время выполнить проверки. Мы хотим, чтобы все значения совпадали, прежде чем показывать пользователю какое-либо уведомление, чтобы сказать им, что их срок выполнения истек.

Оператор + в данном случае преобразует числа с ведущими нулями в их эквиваленты без ведущих нулей, например, 09 -> 9. Это необходимо, потому что числа JavaScript Date никогда не имеют ведущих нулей, но наши данные могут их иметь.

Проверка notified === "no" предназначена для того, чтобы вы получали только одно уведомление на каждый элемент задачи. Когда для каждого объекта задачи генерируется уведомление, его свойство notification устанавливается в "yes", поэтому эта проверка не пройдёт на следующей итерации, посредством следующего кода внутри функции createNotification() (см. Использование IndexedDB для объяснения):

    // now we need to update the value of notified to "yes" in this particular data object, so the
    // notification won't be set off on it again

    // first open up a transaction as usual
    const objectStore = db.transaction(['toDoList'], "readwrite").objectStore('toDoList');

    // get the to-do list object that has this title as its title
    const request = objectStore.get(title);

    request.onsuccess = () => {
      // grab the data object returned as the result
      const data = request.result;

      // update the notified value in the object to "yes"
      data.notified = "yes";

      // create another request that inserts the item back into the database
      const requestUpdate = objectStore.put(data);

      // when this new request succeeds, run the displayData() function again to update the display
      requestUpdate.onsuccess = () => {
        displayData();
      }

Если все проверки пройдут, мы затем запускаем функцию createNotification() для предоставления уведомления пользователю.

      cursor.continue();
    }
  }
}

Последняя строка функции перемещает курсор, что заставляет механизм проверки сроков выполнения выше быть запущенным для следующей задачи, хранящейся в IndexedDB.

Продолжайте проверку!

Конечно, нет смысла запускать функцию проверки сроков выполнения выше только один раз! Мы хотим постоянно проверять все сроки, чтобы увидеть, не наступил ли какой-либо из них. Для этого мы используем setInterval() для запуска checkDeadlines() один раз в секунду:

setInterval(checkDeadlines, 1000);

© 2005–2024 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Checking_when_a_deadline_is_due

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API