How to handle errors with 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]

It can sometimes be difficult to know how to handle exceptions in the different stages of a promise. In Quickstart: Using promises in JavaScript we showed how to use a basic Windows Library for JavaScript promise and handle errors in the then function.

In this topic we show several ways to catch and handle errors when you use promises. You don't need to implement all of these kinds of error handling, but you should pick the type of error handling that best suits your app.

Prerequisites

  • The code in this topic is based on the app created in Quickstart: Using promises in JavaScript, so if you want to follow along you should create the app first. Note that the colors mentioned below appear as described only if you use the "ui-dark.css" stylesheet.

Instructions

Step 1: Turn on JavaScript first-chance exceptions

If you turn on first-chance exceptions, you'll be able to see exceptions without doing anything else to handle them. In Visual Studio, on the Debug menu, select Exceptions, and in the Exceptions dialog box, make sure that JavaScript Runtime Exceptions are selected to be Thrown. After doing this, run the app in debug mode. When an exception is thrown, you see a message box that displays an error. You see this kind of dialog box only when the app is running in the debugger, so users won't see these exceptions.

Step 2: Add an error handler in a then or done function

When you use a promise, you should add a then or done function to explicitly handle the completed value of the promise (that is, the value that's returned by the promise if there is no error) and the error value. Both then and done take three functions (completed, error, and progress) as optional parameters. You use the completed function to perform updates after the promise has been fulfilled, the error function to handle errors, and the progress function to display or log the progress that the promise is making.

There are a couple of differences between then and done:

  • The then function returns a promise, but done doesn't return a value. You can use then functions as intermediate functions in a chain (myPromise().then().then().then()), but done must be the last function.

  • Unhandled exceptions in a then function are silently captured as part of the state of the promise, but unhandled exceptions in a done function are thrown. Both functions can handle exceptions that have been passed to them as part of the state of a promise.

Let's look at a couple of ways to handle errors in then and done functions. In this example, we'll set up a chain of two then functions where the first then passes an error to the second one.

  1. In the TestPromise project, add a second DIV element and give it an ID of "div2":

    <div id="div2">Second</div>
    
  2. In the change handler, remove the error handler from the then function, and get the second DIV element:

    function changeHandler(e) {
        var input = e.target;
        var resDiv = document.getElementById("divResult");
        var twoDiv = document.getElementById("div2");
    
        WinJS.xhr({url: e.target.value}).then(function fulfilled (result) {
                if (result.status === 200) {
                    resDiv.style.backgroundColor = "lightGreen";
                    resDiv.innerText = "Success";
            });
    }
    
  3. Add a second then function and add to it a fulfilled function that changes the color of the second DIV if the result is successful. Add the original error handler to that function.

    function changeHandler(e) {
        var input = e.target;
        var resDiv = document.getElementById("divResult");
        var twoDiv = document.getElementById("div2");
    
        WinJS.xhr({ url: e.target.value })
            .then(function (result) {
                if (result.status === 200) {
                    resDiv.style.backgroundColor = "lightGreen";
                    resDiv.innerText = "Success";
                }
                return result;
            })
            .then(function (result) {
                if (result.status === 200) {
                    twoDiv.style.backgroundColor = "yellow";
                }
            },
            function (e) {
                resDiv.style.backgroundColor = "red";
    
                if (e.message != undefined) {  // If the URL is really malformed or blank.
                    resDiv.innerText = e.message;
                }
                else if (e.statusText != undefined) { // If an XmlHttpRequest is made.
                    resDiv.innerText = e.statusText;
                }
                else {
                    resDiv.innerText = "Error";
                }
            });
        }
    
  4. When you run this code in the debugger, try inputting a URL that isn't valid. You can see that the execution enters the error function in the second then function. As a result, the first DIV should be red, and the second DIV should be black.

In the following example we'll remove the error function from the second then function and add a done function that doesn't have an error handler.

  1. In the TestPromise project, if you have not already added a second DIV element with an element of "div2", you should do so now.

  2. Modify the change handler to remove the error function from the second then and add a done function that turns the second DIV blue. The change handler code should look like this:

    function changeHandler(e) {
        var input = e.target;
        var resDiv = document.getElementById("divResult");
        var twoDiv = document.getElementById("div2");
    
        WinJS.xhr({ url: e.target.value })
            .then(function (result) {
                if (result.status === 200) {
                    resDiv.style.backgroundColor = "lightGreen";
                    resDiv.innerText = "Success";
                }
                return result;
            })
            .then(function (result) {
                if (result.status === 200) {
                    twoDiv.style.backgroundColor = "yellow";
                }
            })
            .done(function (result) {
                if (result.status === 200) {
                    twoDiv.style.backgroundColor = "lightBlue";
            }
        });
    }
    
  3. When you run this code in the debugger, try inputting a URL that isn't valid. You should see a message box displaying an error. Both the first and second DIVs should be black, because none of the code in the then functions or the done function was executed.

Step 3: Add a WinJS.promise.onerror handler

The onerror event occurs whenever a runtime error is caught in a promise. You can use the error handler to set breakpoints while you're debugging or to provide general error handling such as error logging. But because this is a general error handling mechanism, you might not get much detail about the exact code or user input that caused the error.

Here's how to add a general error handler:

  1. In the TestPromise project, remove the then function from the changeHandler code. The resulting changeHandler function should look like this:

    function changeHandler(e) {
        var input = e.target;
        var resDiv = document.getElementById("divResult");
    
        WinJS.xhr({url: e.target.value});
    }
    
  2. Create a general error handling function and subscribe to the onerror event in the app.activated event handler:

    app.activatedHandler = function (args) {
        var input = document.getElementById("inUrl");
        input.addEventListener("change", changeHandler);
        WinJS.Promise.onerror = errorHandler
    }
    function errorhandler(event) {
            var ex = event.detail.exception;
            var promise = event.detail.promise;
        }
    

    The error event provides general error information, such as the exception, the promise in which it occurred, and the current state of the promise (which is always error). But it probably doesn't provide all the information you need to handle the error gracefully. Still, it can provide useful information about errors that you don't explicitly handle elsewhere in your code.

Asynchronous programming in JavaScript