<img src="https://d5nxst8fruw4z.cloudfront.net/atrk.gif?account=J5kSo1IWhd105T" style="display:none" height="1" width="1" alt="">

Nexosis @ Work & Play

Nexosis @ Work & Play

JavaScript Promises and the Nexosis API

JavaScript Promises and the Nexosis API

Guy, our Developer Evangelist, explores how to use JavaScript promises and how to use them with the JavaScript client for the Nexosis Machine Learning API. Promises are baked right into our API so he wants users to take advantage of them.


Introduction

Promises are available for handling asynchronous calls on lots of platforms these days and you've surely heard of them. JavaScript—a language with asynchronicity at its core—is one of them and its implementation is really easy to grok. Today, I'd like to explore how to use the ECMAScript 6 variety of JavaScript promises and how to use them with the JavaScript client for the Nexosis API.

What are promises?

First off, what are these promise things? Promises are a software abstraction around asynchronous callbacks. Confused? Maybe this might be easier to show then to explain. Let's look at some code that doesn't use promises:

doAsyncThing(input, function(result) {
  doAnotherAysncThingUsingResult(result, function(anotherResult) {
    doFinalAsyncThing(anotherResult, function(finalResult) {
      console.log("It is finished: ", finalResult);
    });
  });
});

This code does many asynchronous things. You can tell because of the names of the functions. It's some high-quality sample code. Except, of course, it isn't. This code is overly tab-happy and suffers from what is called The Pyramid of Doom.

I bet you've written code like this. I know I've written code like this.

Promises make this code better because they provide a meaningful abstraction around the nesting; a meaningful abstraction that is aware of the asynchronicity. Take a look at this code:

doAsyncThing(input).then(function(result) {
  return doAnotherAysncThingUsingResult(result);
}).then(function(anotherResult) {
  return doFinalAsyncThing(anotherResult);
}).then(function(finalResult) {
  console.log("It is finished: "", finalResult);
});

Easier to read, yes? It gets better when you start using other ES6 features such as arrow functions.

doAsyncThing(input)
  .then(result => doAnotherAysncThingUsingResult(result))
  .then(anotherResult => doFinalAsyncThing(anotherResult))
  .then(finalResult => console.log("It is finished: "", finalResult));

Much better.


How promises work

So how does this work? Well, these functions, instead of taking a callback function, return a promise. Promises provide a function called .then that you can call to provide that promise with a callback function instead of just passing a callback function into the original function. And .then returns another promise so you can chain them!

If you're thinking at this point this is just syntactic sugar, I agree. I would also say that syntactic sugar is a good enough reason for me to use promises. But, there's more.

Let's say you want to make a couple of asynchronous calls in parallel? Using callbacks, you simply can't do this. You would have to serialize the calls. I suppose you could write some complex method using setTimeout or something like that. Possible, but messy and probably not worth it.

Promises make this easy using Promise.all. Promise.all takes an array of promises and returns a promise that resolves when all the passed-in promises resolve. And, as it turns out, this is a really useful feature if you want to use the Nexosis API.

Let's say you have a series of sessions that you are waiting on and you want to fetch them and return their statuses. You could do it like this:

let NexosisClient = require('nexosis-api-client').default;
let client = new NexosisClient({ key: process.env.NEXOSIS_API_KEY });

let sessionIds = [
  'b3d9efdd-6237-4ecb-8d04-74e2ec5c237b',
  '8fd55d8b-08d0-4923-8d39-96d002ddb9e3',
  '0d62fd70-e47f-497e-924c-f6f00bdc1246',
  'e8bc2b14-4766-4d40-a04d-c11c31fd0049',
  'c1d27958-c7a5-4854-bdb4-31dc31a737dc'
];

Promise.all([
  client.Sessions.get(sessionIds[0]).then(session => session.status),
  client.Sessions.get(sessionIds[1]).then(session => session.status),
  client.Sessions.get(sessionIds[2]).then(session => session.status),
  client.Sessions.get(sessionIds[3]).then(session => session.status),
  client.Sessions.get(sessionIds[4]).then(session => session.status)  
]).then(statuses => console.log("Statuses: ", status));

The check for all of these statuses will happen in parallel. The function passed into .then will not be called until all of them have resolved. And, the output will be in the order they were requested. It's brilliant!

If you're up for a call to .map on the sessionIds array, this code can get even more interesting. Just map the array of session IDs to an array of promises and pass that into Promise.all.

Check it out:

let NexosisClient = require('nexosis-api-client').default;
let client = new NexosisClient({ key: process.env.NEXOSIS_API_KEY });

let sessionIds = [
  'b3d9efdd-6237-4ecb-8d04-74e2ec5c237b',
  '8fd55d8b-08d0-4923-8d39-96d002ddb9e3',
  '0d62fd70-e47f-497e-924c-f6f00bdc1246',
  'e8bc2b14-4766-4d40-a04d-c11c31fd0049',
  'c1d27958-c7a5-4854-bdb4-31dc31a737dc'
];

let promises = sessionIds.map(sessionId => {
  return client.Sessions.get(sessionId)
    .then(session => session.status)
});

Promise.all(promises)
  .then(statuses => console.log("Statuses: ", status));

So that what I've got for today.


Promises are baked right into the Nexosis API so be sure to take advantage of them. They make your code easier to read and easier to think about. Use them!


Ready to start building machine learning applications?

Get your free API key  Talk to an expert


Guy Royse

Guy is one of our developer evangelists at Nexosis. He spends his days sharing with developers why our API is so great and his nights reminiscing about Hogwarts and dreaming of retiring to his dream job: Santa Claus.

 September 11, 2017 @ 2:02 PM |   Technical