Chaining promises (HTML)

[This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation]

You can use promises in a number of different ways. You can chain promise operations by calling then or done on the promise that is returned by the previous then function. You can also track the progress of an operation and do error handling in the different functions you pass as parameters to then or done.

Use then for an intermediate stage of the operation (for example .then().then()), and done for the final stage of the operation (for example .then().then().done()). The differences between then and done are the following:

  • You can chain multiple then functions, because then returns a promise. You cannot chain more than one done method, because it returns undefined.

  • If you do not provide an error handler to done and the operation has an error, an exception is thrown to the event loop. This means that you cannot catch the exception inside a try/catch block, but you can catch it in window.onerror. If you do not provide an error handler to then and the operation has an error, it does not throw an exception but rather returns a promise in the error state.

  • You should prefer flat promise chains to nested ones. The formatting of promise chains makes them easier to read, and it's much easier to deal with errors in promise chains.

    Do this:

    aAsync()
        .then(function () { return bAsync(); })
        .then(function () { return cAsync(); })
        .done(function () { finish(); });
    

    Don't do this:

    // Bad code!
    aAsync().then(function () {
        bAsync().then(function () {
                cAsync().done(function () { finish(); });
        })
    });
    

The examples below chain Windows Runtime methods that return promises, but you can also chain Windows Library for JavaScript promises.

Chaining asynchronous functions

The following example shows how to download a web page to a file by using two asynchronous methods, createFileAsync and startAsync, using chained then functions.

In this example the first then function gets the result and passes it to the completion handler function. A BackgroundDownloader creates the download operation, and the startAsync method starts the download.

The startAsync method returns a promise, so you can chain a second then function by returning the value of the startAsync method in the first completion handler. In the second then function you add a completion handler whose parameter contains the download operation.

WinJS.Utilities.startLog();

// Assign the URI to download from.
var uriExample = new Windows.Foundation.Uri("https://www.microsoft.com");

// Get the folder for temporary files.
var tempFolder = Windows.Storage.ApplicationData.current.temporaryFolder;

// Create the temp file asynchronously.
tempFolder.createFileAsync("tempfile.txt", Windows.Storage.CreationCollisionOption.replaceExisting)
    .then(function (tempFile) {
        // The createFileAsync call succeeded, so start the download operation.
        var downloader = new Windows.Networking.BackgroundTransfer.BackgroundDownloader();
        var transfer = downloader.createDownload(uriExample, tempFile);
        return transfer.startAsync(); 
    })
    .then(
        //Define the function to use when the download completes successfully
        function (result) {
            WinJS.log && WinJS.log("File download complete");
        });

Progress tracking and error handling

You can track the progress of an asynchronous operation by providing a progress function, and you can get detailed information about error conditions by providing an error function. Use then or done to assign the handler functions for progress notifications and error handling.

The following example shows how to download a web page asynchronously to a file by using the startAsync function with progress and error handlers.

 // Assign the URI to download from.
var uriExample = new Windows.Foundation.Uri("https://www.microsoft.com");

// Get the folder for temporary files.
var tempFolder = Windows.Storage.ApplicationData.current.temporaryFolder;

// Create the temp file asynchronously.
tempFolder.createFileAsync("tempfile.txt", Windows.Storage.CreationCollisionOption.replaceExisting)
   .then(function (tempFile) {
       // The createFileAsync call succeeded, so start the download operation.
       var downloader = new Windows.Networking.BackgroundTransfer.BackgroundDownloader();
       var transfer = downloader.createDownload(uriExample, tempFile);
        return transfer.startAsync(); 
   })
   .then(
       //Define the function to use when the download completes successfully
       function (result) {
           WinJS.log && WinJS.log("File download complete");
       },
       // Define the error handling function.
       function (err) {
           WinJS.log && WinJS.log("File download failed.");
       },
       // Define the progress handling function.
       function (progress) {
           WinJS.log && WinJS.log("Bytes retrieved: " + progress.progress.bytesReceived);
       });

When the startAsync function has updates to report, it calls the progress handler with the progress and intermediate result objects. If an error occurs during the asynchronous operation, the error handler is called instead of the completion handler.

Asynchronous programming in JavaScript