Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

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.

Thanks for reading!

Donate to the site — we work hard for you.