JavaScript Async - Composing Promises |
Written by Ian Elliot | |||
Monday, 13 July 2020 | |||
Page 1 of 2 Although async and await are the stars of the async programming world, Promises still have advantages - in particular you can write code which waits for some combination of Promises to resolve. Here we look at how to write your own promise-combining functions. This is an extract from the recently published JavaScript Async: Events Callbacks, Promises & Async/Await. Now Available as a Book:JavaScript AsyncYou can buy it from: Amazon Contents
Composing PromisesOne of the more advanced aspects of using Promises is writing functions that work with multiple Promises in various ways. Many Promise libraries provide utilities such as any, which is fulfilled if any Promise is; some, fulfilled if a fixed number of Promises are; and so on. The JavaScript Promise standard provides just two which we met in the previous chapter.
There is also a variation on all:
Use all if the Promises are dependent and any one failing means they all fail. Use allSettled if you can still continue if some of the Promises have failed. There are also a new Promise combining functions which should be in ECMAScript 2020:
Rather than providing lots of different standard Promise-composing functions, it is simpler to learn how to write your own that do exactly what you want. Usually the problem is how to handle rejects, and this you can tailor to the situation. Our Own Race FunctionAs an example, let's implement our own simple version of the race function, race1, which also returns a Promise that resolves when the first of two Promises resolve. It is always a good idea to try to implement an idea as simply as possible and then extend it to more realistic examples. It turns out that race1 is very easy to write: function race1(p1, p2) { var p = new Promise( function (resolve, reject) { p1.then( function (value) { resolve(value); }, function (error) { reject(error); } ); p2.then( function (value) { resolve(value); }, function (error) { reject(error); } ); }); return p; } All that happens is that we return a new Promise object that is resolved or rejected when any of the Promises provided as arguments resolves or rejects. To make this happen we use the then method of the two Promises to resolve or reject our new Promise. Notice that we don't do anything to stop the other Promises from completing. It is generally difficult to cancel a pending asynchronous operation. Also notice that as a Promise is immutable, we don't need to worry about later Promises settling and trying to set the returned Promise to something different. This can be used to get the first Promise to resolve or reject: myPromise1 = delay(1000, 0); myPromise2 = delay(2000, 0); myTotalPromise = race1(myPromise1, myPromise2); myTotalPromise.then( function (value) { console.log("success"); console.log(value); }, function (value) { console.log("fail"); console.log(value); }); In this case you will see a delay of 2000ms. Of course, you could add the race1 function as a method to the Promise object. Any Number Of PromisesExtending the race function to work with any number of Promises can be done using JavaScript's iterable i.e an object that behaves like an Array. The key is to use the map member which is supported by most browsers from IE 8 on: function race1(args) { var p = new Promise( function (resolve, reject) { args.map( function (p) { p.then( function (value) { resolve(value); }, function (error) { reject(error); } ); }); }); return p; } You can see that all that happens is that the then method is used for each of the elements in the array. If you don’t want to use map you can use a for loop instead. To make this work you have to remember to pass an array of Promises: myPromise1 = delay(2000, 0); myPromise2 = delay(1000, 0); myTotalPromise = race1([myPromise1, myPromise2]); This is an implementation of the standard race function, but it is generally held that it isn't very useful as it will return the first function to complete, even if it rejects. |
|||
Last Updated ( Saturday, 12 September 2020 ) |