Using JavaScript Promises in KeyLines

This post was published some time ago.

It's still useful, but it's worth searching for up-to-date information in one of our more recent blog posts.

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.

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

Want to try KeyLines’ Promises for yourself? Request a trial to get started.

How can we help you?

Request trial

Ready to start?

Request a free trial

Learn more

Want to learn more?

Read our white papers

“case

Looking for success stories?

Browse our case studies

Registered in England and Wales with Company Number 07625370 | VAT Number 113 1740 61
6-8 Hills Road, Cambridge, CB2 1JP. All material © Cambridge Intelligence 2024.
Read our Privacy Policy.