How to Use jQuery Deferred .progress()

jQuery Deferred objects provide a way to handle asynchronous operations, and the .progress() method is specifically designed to listen for intermediate updates before the task completes or fails. This article explains how to implement .progress() to capture real-time status updates emitted by deferred.notify(), complete with code examples, common usage patterns, and key operational behaviors.

Understanding the .progress() Method

When dealing with long-running tasks, such as file uploads, data processing, or multi-step animations, users often need feedback before the final completion. While .done() handles resolution and .fail() handles errors, .progress() attaches handlers that execute whenever the Deferred object calls its notify() or notifyWith() methods.

Syntax:

deferred.progress( progressCallback [, progressCallback ] )

Implementing .progress() Step-by-Step

To track updates, you must create a Deferred object, emit progress via notify(), and register the listener using .progress() on either the Deferred or its generated Promise.

1. Create an Asynchronous Function Using notify()

The source operation uses deferred.notify() to emit data to any listeners:

function performTask() {
    var deferred = $.Deferred();
    var count = 0;

    var interval = setInterval(function() {
        count += 25;
        
        // Send a progress update
        deferred.notify(count);

        if (count >= 100) {
            clearInterval(interval);
            deferred.resolve("Task completed successfully!");
        }
    }, 500);

    return deferred.promise();
}

2. Listen for Progress Updates

Call the function and attach the .progress() handler alongside standard completion handlers:

performTask()
    .progress(function(percent) {
        console.log("Current progress: " + percent + "%");
        $("#progress-bar").css("width", percent + "%").text(percent + "%");
    })
    .done(function(message) {
        console.log(message);
    })
    .fail(function(error) {
        console.error("Task failed: " + error);
    });

Key Behaviors and Rules

  • Notification Cutoff: Once a Deferred object is resolved via resolve() or rejected via reject(), calls to notify() are ignored. Progress callbacks will no longer execute.
  • Late Binding: If you attach a .progress() callback after notify() calls have already been executed, the callback will not retroactively receive past notifications. It only executes for subsequent notify() calls.
  • Passing Data: You can pass any data type through notify(), including numbers, strings, or complex objects containing detailed status information (such as { stage: 'uploading', bytesSent: 1048576 }).
  • Chaining: The .progress() method returns the Deferred or Promise object, allowing it to be chained directly with .then(), .done(), .fail(), and .always(). Alternatively, you can pass progress callbacks as the third argument to the .then() method: deferred.then(doneFilter, failFilter, progressFilter).