OdeToCode IC Logo

JavaScript Promises and Error Handling

Thursday, October 1, 2015 by K. Scott Allen

Errors in asynchronous code typically require a messy number of if else checks and a careful inspection of parameter values. Promises allow asynchronous code to apply structured error handling. When using promises, you can pass an error handler to the then method or use a catch method to process errors. Just like exceptions in regular code, an exception or rejection in asynchronous code will jump to the nearest error handler.

As an example, let’s use the following functions which log the execution path into a string variable.

var log = "";

function doWork() {
    log += "W";
    return Promise.resolve();
}

function doError() {
    log += "E";
    throw new Error("oops!");
}

function errorHandler(error) {
    log += "H";
}

We’ll use these functions with the following code.

doWork()
    .then(doWork)
    .then(doError)
    .then(doWork) // this will be skipped
    .then(doWork, errorHandler)
    .then(verify);
    
  function verify() {
    expect(log).toBe("WWEH");
    done();
}

The expectation is that the log variable will contain “WWEH” when the code finishes executing, meaning the flow of calls with reach doWork, then doWork, then doError, then errorHandler. There are two observations to make about this result, one obvious, one subtle.

The first observation is that when the call to doError throws an exception, execution jumps to the next rejection handler (errorHandler) and skips over any potential success handlers. This behavior is obvious once you think of promises as a tool to transform asynchronous code into a procedural flow of method calls. In synchronous code, an exception will jump over statements and up the stack to find a catch handler, and the asynchronous code in this example is no different.

What might not be immediately obvious is that the verify function will execute as a success handler after the error. Just like normal execution can resume in procedural code after a catch statement, normal execution can resume with promises after a handled error. Technically, the verify function executes because the error handler returns a successfully resolved promise. Remember the then method always returns a new promise, and unless the error handler explicitly rejects a new promise, the new promise resolves successfully.

A promise object also provides a catch method to handle errors. The last code sample could be written with a catch statement as follows. ]

doWork()
    .then(doWork)
    .then(doError)
    .then(doWork) 
    .then(doWork)
    .catch(errorHandler)
    .then(verify);

The catch method takes only a rejection handler method. There can be a difference in behavior between the following two code snippets:

.then(doWork, errorHandler)

… and …

.then(doWork)
.catch(errorHandler)

In the first code snippet, if the success handler throws an exception or rejects a promise, execution will not go into the error handler since the promise was already resolved at this level. With catch, you can always see an unhandled error from the previous success handler.

Finally, imagine you have a rejected promise in your code, but there is no error handler attached. You can simulate this scenario with the following line of code.

Promise.reject("error!");

Some native environments and promise polyfills will warn you about unhandled promise rejections by displaying a message in the console of the developer tools. An unhandled promise rejection means your application could be missing out on a critical error!

C# Fundamentals with Visual Studio 2015

Wednesday, September 30, 2015 by K. Scott Allen

I've created a new C# Fundamentals course with Visual Studio 2015. This course, like the previous course on Pluralsight, doesn't focus so much on syntax and language quirks. Instead,  I like to focus on what I consider the important fundamentals, like understanding the difference between reference types and value types, how types live inside assemblies, some basic design tips, and more.

image

Enjoy!

JavaScript Promise API

Tuesday, September 29, 2015 by K. Scott Allen

Yesterday's post looked at chaining promises. Now, let's take a closer  look at the API available for promises.

A Promise in JavaScript offers a few static methods you can use as convenience methods. For example, when you need to return a promise to a caller but you already have a value ready, the resolve method is handy.

let doAsyncWork = function () { 
    // note: no async work to perform 
    return Promise.resolve(10); 
}; 

doAsyncWork().then(result => { 
    expect(result).toBe(10); 
    done(); 
});

Likewise, the reject method will deliver a ready result to an error handler.

let doAsyncWork = function () {
    return Promise.reject("error!");
};

doAsyncWork().then(() => { }, message => {
    expect(message).toBe("error!");
    done();
});

The race method will combine multiple promises into a single promise. The single promise will resolve when the first of the multiple promises resolves. It’s a race to see who finishes first!

let slowExecutor = function (resolve, reject) {
    setTimeout(() => {
        resolve(9);
    }, 250);
};

let fastExecutor = function (resolve, reject) {
    setTimeout(() => {
        resolve(6);
    }, 100);
};

let p1 = new Promise(slowExecutor);
let p2 = new Promise(fastExecutor);

let p3 = Promise.race([p1, p2]);

p3.then(result => {
    expect(result).toBe(6);
    done();
});

In contrast to race, the all method will combine multiple promises into a single promise, and the single promise will resolve when all of the multiple promises resolve.

let slowExecutor = function (resolve, reject) {
    setTimeout(() => {
        resolve(9);
    }, 250);
};

let fastExecutor = function (resolve, reject) {
    setTimeout(() => {
        resolve(6);
    }, 100);
};

let p1 = new Promise(slowExecutor);
let p2 = new Promise(fastExecutor);

let p3 = Promise.all([p1, p2]);

p3.then(result => {
    expect(result[0]).toBe(9);
    expect(result[1]).toBe(6);
    done();
});

The results are delivered in the same order as the promises that produce each result appear in the array parameter to all.

Of course, not all promises resolve successfully, so the next post in this series will look at error handling with promises.

Chaining Promises in JavaScript

Monday, September 28, 2015 by K. Scott Allen

In part 1 we looked at the basic use of native promises in JavaScript 2015. In this post we'll look at how to compose and chain promises.

Chaining promises can make asynchronous code flow synchronously. For example, consider the following calculate method which delivers a result using a promise.

let calculate = function (value) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(value + 1);
        }, 0);
    });
}; 

Imagine you need to invoke the calculate method four times, and each time you invoke calculate you need to pass the result from the previous call into the next call (this would simulate HTTP APIs where you need to make multiple requests, all dependent on one another, to fetch all of data required for a page). With promises, the series of method calls could look like the following.

calculate(1)
    .then(calculate)
    .then(calculate)
    .then(calculate)
    .then(verify);

function verify(result) {
    expect(result).toBe(5);
    done();
};

The above code also verifies (with Jasmine) that the final result is the value 5, because compute will add 1 to the result on each invocation. The code works because each call to the then method of a promise will result in a new promise. You might think this happens because the calculate method returns a new promise, but a new promise will appear even if the function passed to the then method doesn’t explicitly produce a promise. As an example, let’s replace one call to calculate with an arrow function that simply returns result + 1.

calculate(1)
    .then(calculate)
    .then(result => result + 1)
    .then(calculate)
    .then(verify);

function verify(result) {
    expect(result).toBe(5);
    done();
};

The result of the above code is still the value 5, and the object returned from the then call with an arrow function is still a promise, because the then method ensures that the success handler’s return value is wrapped into a promise. If the success handler does not return a value, the new promise will deliver the value undefined to the next handler.

To see how easy it is to create a new promise when you already have a value to resolve the promise, let’s look at the promise API in tomorrow's post.

Promises in ES2015 Part 1

Thursday, September 3, 2015 by K. Scott Allen

Asynchronous programming has been a hallmark of JavaScript programming since the beginning. Examples include waiting for button clicks, waiting for timers to expire, and waiting for network communications to complete. For most of JavaScript’s life, we’ve implemented these waiting activities using callback functions.

let calculate = function(callback) {

    setTimeout(() => {
        callback("This is the result"); 
    }, 0);

};

calculate(result => {
    expect(result).toBe("This is the result");
    done(); // done is required to complete 
            // this async test when using Jasmine
});

Over the years, as JavaScript applications grew in complexity, unofficial specifications started to emerge for a different approach to asynchronous programming using promises. A promise is an object that promises to deliver a result in the future. Promises are now an official part of the JavaScript language and offer advantages over the callback approach to asynchronous programming. Error handling is often easier using promises, and promises make it easier to compose multiple operations together. These advantages make code easier to read and write, as we’ll see in the upcoming posts.

Promise Objects

We need to look at promises from two different perspectives. The first perspective is the perspective of the code consuming a promise to wait for an asynchronous activity to complete. The second perspective is the perspective of the code responsible for producing a promise and managing an asynchronous activity behind the scenes.

A Promise Producer

In the previous example, the calculate function delivers a result asynchronously after a timer expires by invoking a callback function and passing along the result. When using promises, the calculate function could look like the following.

let calculate = function () {

    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(96);
        }, 0);
    });

};

Instead of taking a callback function as a parameter, the calculate method returns a new promise object. The Promise constructor takes a parameter known as the executor function. The previous code sample implements the executor function as an arrow function. The executor function itself takes two arguments. The first argument is the resolve function. Invoking the resolve function fulfills the promise and delivers a successful value to anyone who is waiting for a result from the promise. The second function is the reject function. Invoking the reject function rejects the promise, which signals an error. The above code always resolves the promise successfully and passes along a result of 96.

A Promise Consumer

Instead of passing a callback function to the calculate method, the consumer now invokes the calculate method and receives a promise object. A promise object provides an API that allows the consumer to execute code when the producer resolves or rejects the promise. The most important part of the promise API to a consumer is the then method. The then method allows the consumer to pass function arguments to execute when the promise resolves or rejects. The consumer code for calculate can now look like the following.

let success = function(result) {
    expect(result).toBe(96);
    done();
};

let error = function(reason) {
    // ... error handling code for a rejected promise
};

let promise = calculate();
promise.then(success, error);

The first argument to the then method is the success handler, while the second argument is the error handler.

On the surface, using promises might seem more involved than using simple callback functions. However, promises really start to shine when composing operations, and when handling errors. We’ll look at these topics in the coming posts.

Building Applications with Aurelia

Wednesday, September 2, 2015 by K. Scott Allen

Earlier this summer I released a “Building Applications with Aurelia” course on Pluralsight.

Building Applications with Aurelia

I’ve enjoyed working with Aurelia since the early days and seeing how developers combine Aurelia with other frameworks and libraries. You can follow the latest news, too, by watching the Aurelia blog.

Delegating yield in JavaScript

Tuesday, September 1, 2015 by K. Scott Allen

In an earlier post we looked at generator functions in JavaScript.

Generator methods can call into other generator methods, and yield values received from another generator method. A generator can even unroll or flatten another generator’s result into it’s own iterator using yield*. As an example, consider the following generator method which yields two strings.

let inner = function*() {
    yield "Hello";
    yield "there";
}

The next generator will call the inner generator using the yield* syntax.

let outer = function*() {
    yield* inner();
    yield "World";
}

The yield* syntax will flatten the result from inner so that the outer generator yields three strings.

var result = Array.from(outer());
expect(result).toEqual(["Hello", "there", "World"]);

If the outer generator used yield instead of yield*, the result of outer would be inner’s iterator, followed by the string “World”.