Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Промисы: $.Deferred и then

Объект запроса, который возвращают $.ajax() и $.get(), — это промис jQuery: обещание результата, который появится позже. Такие же промисы можно создавать самому через $.Deferred(). В jQuery 3 они совместимы с обычными промисами JavaScript, которые вы знаете из раздела учебника об асинхронности.

Свой промис

$.Deferred() создаёт отложенный объект. Его выполняют методом resolve() или отклоняют методом reject(), а наружу отдают только промис — promise(). Здесь он заменяет запрос к серверу: данные «приходят» через полсекунды.

<ul id="tasks"></ul>
 
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script>
  function loadTasks() {
    const deferred = $.Deferred();
 
    setTimeout(function () {
      deferred.resolve(['Купить хлеб', 'Позвонить Олегу']);
    }, 500);
 
    return deferred.promise();
  }
 
  loadTasks().done(function (tasks) {
    for (const title of tasks) {
      $('#tasks').append($('<li>').text(title));
    }
    console.log('Загружено задач:', tasks.length);
  });
</script>

Через полсекунды список заполняется, в выводе — «Загружено задач: 2». Функцию loadTasks() позже можно заменить на $.getJSON('/api/tasks'), а остальной код не изменится.

Ошибка: reject и fail

<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script>
  function saveTask(title) {
    const deferred = $.Deferred();
    if (title === '') {
      deferred.reject('Пустое название');
    } else {
      deferred.resolve({ id: 3, title: title });
    }
    return deferred.promise();
  }
 
  saveTask('')
    .done(function (task) { console.log('Сохранено:', task.title); })
    .fail(function (reason) { console.log('Ошибка:', reason); });
</script>

В выводе «Ошибка: Пустое название». С непустой строкой сработал бы done().

then: цепочка шагов

then() принимает функцию для результата и возвращает новый промис. Значение, которое вернула функция, передаётся следующему шагу:

<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script>
  function loadTasks() {
    return $.Deferred().resolve([
      { title: 'Купить хлеб', done: true },
      { title: 'Позвонить Олегу', done: false },
    ]).promise();
  }
 
  loadTasks()
    .then(function (tasks) {
      return tasks.filter((task) => !task.done);
    })
    .then(function (open) {
      console.log('Осталось сделать:', open.length); // 1
    });
</script>

Разница с done(): done() всегда получает исходный результат, а then() передаёт дальше то, что вернула функция.

Несколько запросов: $.when

$.when() ждёт, пока выполнятся все переданные промисы:

<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script>
  const tasks = $.Deferred().resolve(['Купить хлеб']).promise();
  const user = $.Deferred().resolve('Анна').promise();
 
  $.when(tasks, user).done(function (taskList, name) {
    console.log(name + ', задач у вас:', taskList.length);
  });
</script>

С настоящими запросами $.when($.getJSON(...), $.getJSON(...)) передаёт в функцию по массиву на каждый запрос: данные, статус и объект запроса.

await

Промисы jQuery 3 можно ждать через await и передавать в Promise.all():

// script.js
const tasks = await $.getJSON('/api/tasks');
console.log(tasks.length);

Частая ошибка

Отдавать наружу сам объект $.Deferred() вместо deferred.promise(). Тогда любой код снаружи может вызвать resolve() или reject() и завершить операцию за неё. Возвращайте из функции только промис.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.