Using JavaScript Promises in KeyLines: Part 1

18th January, 2017

Introducing asynchronous code and basic Promises

In KeyLines 3.3, we announced support for JavaScript Promises. We know some of you have been coding with Promises for some time, so we wanted to give you the option to use them in KeyLines too.

This is the first of two blog posts that’ll introduce Promises and show you how to use them.

Understanding Asynchronous Programming

To understand promises, you must first understand the concept of asynchronous programming.

Regular synchronous code executes functions one after another, waiting for function 1 to finish before executing function 2, and so on. In this example, if function 2 takes time to complete, e.g. an AJAX call or DOM change, it delays the execution of function 3, and every function further down the chain:

Synchronous code executes functions one after another, waiting for one to complete before starting the next
Synchronous code executes functions one after another, waiting for one to complete before starting the next

Take KeyLines chart.layout function as an example. With a synchronous implementation, a computationally-heavy chart layout across thousands of nodes could create significant delays, block further functions, and frustrate users.

JavaScript, on the other hand, can be asynchronous (async). Functions are still executed in order, but the program doesn’t wait for the previous function to complete before beginning the next:

Unless you tell it otherwise, async code will not wait for one function to complete before starting the next
Unless you tell it otherwise, async code will not wait for one function to complete before starting the next

Instead, time-consuming functions like chart.layout are initiated with a ‘callback’ parameter (more on that later), and KeyLines moves on to the next function. It’s faster and more responsive for users.

The execution of four functions in synchronous and asynchronous code
The execution of four functions in synchronous and asynchronous code

Managing asynchronous workflows

In scenarios where we don’t want multiple functions to overlap, we can use callbacks. Callbacks are function references passed as a parameter to an async function. These callback functions will themselves be called by the async function once it’s completed its operations.

Say, for example, you want to load new data into a chart and instantly run a layout. Both chart.load and chart.layout are async and require time.

Without correct callback management, we may run a layout before the data has finished loading. To force the browser to run this code sequentially, we pass the layout callback in the load function as an argument, like this:

function doLayout () {
  chart.layout("standard");
}
chart.load(data, doLayout);

Now the layout won’t be performed until after the load function is complete.

This extra power and flexibility is great for developers, but in real-world applications with complex logic, things can soon get complicated with endless nested callbacks. To avoid this ‘Callback Hell’ – we use Promises…

What is a JavaScript Promise?

Promises are an alternative way to manage async workflows. They’ve been around for some time, but were first natively supported in the ES6 iteration of JavaScript.

Instead of passing a function as an argument to another function (the callback approach), we tell the code to create placeholders for the as-yet-unknown results.

An async function will return a new Promise object. The Promise object is then resolved if the function is successful, or rejected if it is unsuccessful. As we’ll see, they also help you write cleaner, less error-prone code.

How to use Promises in your KeyLines code

Implementing Promises in your KeyLines code is easy. First, use KeyLines.promisify() to make the API Promise-aware:

// Enable Promises
KeyLines.promisify();

And you’re ready to start. For example:

chart.load( data )
 .then( function(){
   return chart.layout();
 });

KeyLines also supports third party implementations of Promises, like the Angular $q object:

// Enable Promises
KeyLines.promisify( Q.Promise );

// Use Promises
chart.load( data )
 .then( function(){
   return chart.layout();
 });

Actions in sequence

The advantages of using Promises are clear when we try to organize code into a sequence of functions. Let’s work through the following actions:

  • Load some data
  • Zoom to fit
  • Run a layout
  • Apply a ‘ping’ effect on a specific node

These need to run in order – we can’t ‘ping’ a node before it’s loaded. We can sequence them by chaining Promises:

chart.load(data)
  .then(() => chart.zoom('fit'))
  .then(() => chart.layout('standard'))
  .then(() => chart.ping('d5'));

Actions in parallel

In this example, we’ll use promises to change an item’s color and run a layout simultaneously.

Use Promise.all() to delay execution until both functions are resolved:

// a new function to animate a color change of an item
function changeColor( newColor ){
 return function(){
   return chart.animateProperties( {id: ‘itemId’, c: newColor }, time: 800 );
 };
}

// run a layout and change color simultaneously
var actions = [ layout(), changeColor( ‘red’ ) ];

Promise.all( actions ).then( function(){
 // execute this code when both actions have completed
 ...
});

Handling Errors with Promises

And finally, a quick word on handling errors. It’s not always easy to check for errors in async code using callbacks. Using Promises simplifies this. We can use the Promise object’s ‘resolved’ or ‘rejected’ information, via a .catch method, to handle errors:

chart.load( data)
  .then( function(){
    return chart.layout(‘wrong’);
  })
  .catch( function(){
    // handle a possible error here
  })
  .then( function(){
    // now carry on!
    return chart.layout(‘standard’);
  });

Try it yourself

KeyLines Promises are currently in beta development. We’d like you to try them and send your feedback to support@keylines.com.

Request a free trial and get started with the KeyLines SDK.

Subscribe to our newsletter

Get occasional data visualization updates, stories and best practice tips by email