Промисы: $.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() и завершить операцию за неё. Возвращайте из функции только промис.