Promises

Of all the methods we'll cover in this chapter to organize code, promises are probably my favorite. They're really simple to use and form the foundation of the await/async pattern I use all over this book. In short, a promise is an object that represents a value that may be available now, never, or sometime between those two extremes. Here's a short example:

const p = new Promise((resolve, reject) => { 
d3.json('http://www.aendrew.com/api/1.json', (err, data1) => {
if (err) {
reject(err);
} else {
resolve(data1);
}
});
});

p.then(data => console.log(data.newEndpoint))
.catch(err => console.error(err));

We instantiate a new promise and assign it to p. The Promise constructor takes a callback containing two functions: resolve and reject. We pass errors to reject and data to resolve. Then, outside the Promise callback, we can use the Promise.prototype.then() method of p to do something once the promise is resolved, or handle errors using Promise.prototype.catch(). This is a pretty silly example though, because d3-request doesn't return promises, and you have to program around its callback style of design. Let's use Fetch from now on.

With that in mind, what if you have two promises you want to resolve before continuing? With callbacks, you'd probably still want to nest the requests or do something clever with events even if the second doesn't need any data from the first to succeed. With promises, we can resolve multiples of them using one call to Promise.all:

Promise.all([fetch('./data1.json'), fetch('./data2.json')]) 
.then(([data1, data2]) => [data1.json(), data2.json()])
.then(([data1, data2]) => {
console.log(data1); // Resolved data1.json
console.log(data2); // Resolved data2.json
});

If you remember from way back in Chapter 1, Getting Started with D3, ES2017, and Node.js, we can use Fetch to return a promise containing a request, which we can turn into another promise containing the resolved data parsed a particular way (text, binary blob, and JSON). All we're doing here is combining promises using Promise.all and resolving them as we would before.

But then again, why are we even bothering with the old promise syntax when we have the new hotness known as async/await?:

async function getDataAsync() { 
const data1 = await (await fetch('./data1.json').json();
const data2 = await (await fetch(`./data${data1}.json`).json();

console.log(data1);
console.log(data2);
}

Holy hell, that's utterly gorgeous! What's going on here?

Any function marked with async will ultimately return a promise of some variety. Even if you don't specify a value to return (such as in our preceding example), a function defined with the async keyword will still return a promise. What's cool, however, is that you can then use the await keyword to wait for a promise to resolve instead of having to use Promise.prototype.then(). As a result, the use of the Promise API is very transparent and lets you write asynchronous code in a style very familiar to anyone with experience of non-asynchronous languages.

I will refrain from giving an in-depth example of how to use Promises here because we've used them already and will continue to do so. By the end of this book, you'll be well-versed in how they work.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset