Promises: $.Deferred and then
The request object returned by $.ajax() and $.get() is a jQuery promise: a promise of a result that will appear later. You can create the same promises yourself with $.Deferred(). In jQuery 3 they are compatible with the regular JavaScript promises you know from the tutorial chapter on async code.
Your own promise
$.Deferred() creates a deferred object. It is fulfilled with resolve() or rejected with reject(), and only its promise, promise(), is handed out. Here it stands in for a server request: the data "arrives" after half a second.
<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(['Buy bread', 'Call Oleg']);
}, 500);
return deferred.promise();
}
loadTasks().done(function (tasks) {
for (const title of tasks) {
$('#tasks').append($('<li>').text(title));
}
console.log('Tasks loaded:', tasks.length);
});
</script>After half a second the list fills in, and the output says "Tasks loaded: 2". Later loadTasks() can be replaced with $.getJSON('/api/tasks'), and the rest of the code stays the same.
Errors: reject and 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('Empty title');
} else {
deferred.resolve({ id: 3, title: title });
}
return deferred.promise();
}
saveTask('')
.done(function (task) { console.log('Saved:', task.title); })
.fail(function (reason) { console.log('Error:', reason); });
</script>The output is "Error: Empty title". With a non-empty string done() would run.
then: a chain of steps
then() takes a function for the result and returns a new promise. The value the function returns goes to the next step:
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script>
function loadTasks() {
return $.Deferred().resolve([
{ title: 'Buy bread', done: true },
{ title: 'Call Oleg', done: false },
]).promise();
}
loadTasks()
.then(function (tasks) {
return tasks.filter((task) => !task.done);
})
.then(function (open) {
console.log('Still to do:', open.length); // 1
});
</script>The difference from done(): done() always receives the original result, while then() passes on whatever the function returned.
Several requests: $.when
$.when() waits until all the given promises are fulfilled:
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script>
const tasks = $.Deferred().resolve(['Buy bread']).promise();
const user = $.Deferred().resolve('Anna').promise();
$.when(tasks, user).done(function (taskList, name) {
console.log(name + ', your tasks:', taskList.length);
});
</script>With real requests, $.when($.getJSON(...), $.getJSON(...)) passes one array per request to the function: the data, the status and the request object.
await
jQuery 3 promises can be awaited and passed to Promise.all():
// script.js
const tasks = await $.getJSON('/api/tasks');
console.log(tasks.length);Common mistake
Handing out the $.Deferred() object itself instead of deferred.promise(). Then any outside code can call resolve() or reject() and finish the operation for it. Return only the promise from your function.