Creating a new Promise

The preceding section explained how to properly chain and use Promises – which is the absolute basics of what you need to know to use Office.js. However, you may find that sometimes you need to create a brand new Promise of your own. How and why would you do this?

With the technical prerequisites out of the way: Let’s start with why would you want to create a Promise in the first place. Generally, this will be when you have an asynchronous method that uses callbacks, and which you’d like to wrap with a Promise facade, so that it can fit in with the rest of your elegant workflow.

A very simple HTML example: Let’s say that you want to take a particular object, and switch its background to reflect a color of the rainbow for a couple of seconds, before switching to the next one.

In JavaScript, you have setTimeout, which is a function that takes in an action and invokes it after X many milliseconds. This means that if you really wanted to, you could do the rainbow-switcher as follows:

A pyramid of setTimeout-s
 1 function createRainbow() {
 2     var $rainbow = $('#rainbow');
 3     object.css('background', 'red');
 4     
 5     setTimeout(function() {
 6         object.css('background', 'orange');
 7         setTimeout(function() {
 8             object.css('background', 'yellow');
 9             setTimeout(function() {
10                 object.css('background', 'green');
11                 setTimeout(function() {
12                     object.css('background', 'blue');
13                     setTimeout(function() {
14                         object.css('background', 'indigo');
15                         setTimeout(function() {
16                             object.css('background', 'violet');
17                         }, 2000);
18                     }, 2000);
19                 }, 2000);
20             }, 2000);
21         }, 2000);
22     }, 2000);
23 }

Needless to say, this pyramid is less than ideal, as it becomes visually difficult to match the beginning and end of each function. Adding a new step in the pyramid, somewhere in the middle, also becomes tedious, as you need to carefully examine the code for the right injection point, and you need to indent all the rest of the impacted code. Finally, while not relevant for this example (setTimeout never errors out), error-handling also becomes very tedious in this callback-style system, as you need to add an error callback at each layer, and it’s not easy to abort the rest of the processing in case of an error. What you’d ideally want is to list out the steps in linear function – which is what Promises are meant for.

Let’s create a pause function, which takes in the number of seconds to pause for, and returns a Promise that is resolved after the appropriate number of seconds has elapsed. We will then be able to use it to fix the setTimout pyramid above.

To create a Promise, all you need to do is invoke the Promise constructor, passing in a function that represents the action you want taken. From somewhere within the action, you call resolve(optionalSuccessValue) or reject(error). And so:

A pause Promise
1 function pause(seconds) {
2     return new OfficeExtension.Promise(function(resolve, reject) {
3         setTimeout(function() {
4             resolve();
5         }, seconds * 1000);
6     });
7 }

By the way, in this particular case, the Promise has no way to fail – so we don’t really need the reject callback. And moreover, because we’re calling setTimout with an anonymous function whose sole job is to call another function, we can even simplify the pause function further, if we wanted to:

A leaner pause Promise
1 function pause(seconds) {
2     return new OfficeExtension.Promise(function(resolve) {
3         setTimeout(resolve, seconds * 1000);
4     });
5 }

Armed with this newly-created function for creating Promsies, we can now solve the rainbow scenario much more satisfactorily:

A Promise-ful rainbow
 1 function createRainbow() {
 2     var $rainbow = $('#rainbow');
 3     
 4     object.css('background', 'red');
 5     
 6     return pause(2)
 7         .then(function() {
 8             object.css('background', 'orange');
 9             return pause(2);
10         })
11         .then(function() {
12             object.css('background', 'yellow');
13             return pause(2);
14         })
15         ...
16         .then(function() {
17             object.css('background', 'violet');
18         });
19 }

By the way, you’ll notice that there is an anomaly between how the first (red-background) call is made, relative to the rest. If we want to standardize our calls to all look the same, we could create an initial Promise, so that even the red-background call could be part of a .then. To create this starter Promise, simply call Promise.resolve():

An more consistent Promise-ful rainbow, with a Promise.resolve() at the start
 1 function createRainbow() {
 2     var $rainbow = $('#rainbow');
 3     
 4     return Promise.resolve()
 5         .then(function() {
 6             object.css('background', 'red');
 7             return pause(2);
 8         })
 9         .then(function() {
10             object.css('background', 'orange');
11             return pause(2);
12         })
13         ...
14 }

With TypeScript 2.1’s async/await feature – discussed in more detail in section TypeScript & Async/Await – you can make these calls even cleaner:

An Promise-ful rainbow with TypeScript’s async/await
 1 async function createRainbow() {
 2     var $rainbow = $('#rainbow');
 3     
 4     object.css('background', 'red');
 5     await pause(2);
 6 
 7     object.css('background', 'orange');
 8     await pause(2);
 9 
10     ...
11 
12     object.css('background', 'indigo');
13     await pause(2);
14 
15     object.css('background', 'violet');
16 }

Let’s get back to the pause function definition. As we saw, in order to wrap a callback-style function with a Promise, you create a new Promise, and put the entirety of the former callback-style code
within the Promises’s constructor. Then, somewhere within the callback code (now residing in the constructor), whenever the asynchronous operation completes, you would call the resolve or reject
functions. For resolve, you can pass in an optional value, which the caller of the Promise will be able to read within invoking .then on the Promise (similarly, you can pass an Error object to the reject function, which can be accessed by the caller as part of a .catch). So for example, if we wanted a Promise that, after a pause, returns a random number, we could do:

A patience-strengthening random-number generator
 1 function getRandomNumberAfterPause(seconds) {
 2     return new OfficeExtension.Promise(function(resolve, reject) {
 3         setTimeout(function() {
 4             resolve(Math.floor(Math.random() * 100));
 5         }, seconds * 1000);
 6     });
 7 }
 8 
 9 // To use this Promise, call:
10 getRandomNumberAfterPause(5 /*seconds*/)
11     .then(function(result) {
12         console.log("After waiting for 5 seconds, " +
13             "we now have our lucky number: " + result);
14     });

 

Let’s do one final example of a Promise-creation – this time, for a much more “real-world” use-case, of wrapping a jQuery AJAX call into a Promise. The concrete scenario is: given a list of stock name inputs (["MSFT", "INTC", ...]), call a web service, and return a dictionary of the latest stock prices ({ "MSFT": 59.00, "INTC": 34.94, ... }).

Creating a new Promise as a jQuery AJAX wrapper
 1 function getStockData(stockNamesList) {
 2     var quotedCommaSeparatedNames = stockNamesList
 3         .map(function(name) {
 4             return '"' + name + '"'
 5         })
 6         .join(",");
 7 
 8     var url = '//query.yahooapis.com/v1/public/yql';
 9     var data = 'q=' +
10         encodeURIComponent(
11             'select * from yahoo.finance.quotes ' +
12             'where symbol in (' + quotedCommaSeparatedNames + ')'
13         ) +
14         "&env=http%3A%2F%2Fdatatables.org" +
15         "%2Falltables.env&format=json";
16 
17     return new OfficeExtension.Promise(function(resolve, reject) {
18         $.ajax({
19             url: url,
20             dataType: 'json',
21             data: data,
22             timeout: 5000
23         })
24             .done(function(result) {
25                 console.log("Web request succeeded");
26                 var stockDataDictionary = {};
27                 var stockDataArray = result.query.results.quote;
28                 stockDataArray.forEach(data => {
29                     var stockName = data['symbol']
30                     stockDataDictionary[stockName] = data['Ask'];
31                 });
32 
33                 resolve(stockDataDictionary);
34             })
35             .fail(function(error) {
36                 console.log("Web request failed:");
37                 console.log(url);
38 
39                 reject(new Error(error.statusText));
40             });
41     });
42 }

 

I should note that it’s possible to use a library like Q (https://github.com/kriskowal/q) to wrap the jQuery AJAX call. Personally, though, I’ve found it easier and more flexible to do my own wrapping, especially when I want to do some post-processing on the server response, like I did above.