loading...

Using ES6 array destructing with Promises

mrm8488 profile image Manuel Romero ・3 min read

(originally posted more than one year ago on Medium)

As you know, some months ago, it was announced the new features of ES6 or EcmaScript2015. In the beginning not all of them were implemented by some browsers or Node.js, but we had tools like Babel.js to allow us to program in ES6 and then it would translate the code to ES5.
I am a Node.js developer and when I read the list of new features of ES6, there was one of them that could help me to continue fighting against callback hell and improve the readability of Promises. In particular, the readability of Promise.all() that, as we know, launches an array of promises in parallel. This feature is array destructing.

This is the way I worked with Promise.all() before using ES6 array destructing:

"use strict";

const getUserInfo = () =>
  new Promise((resolve, reject) => {
    resolve({
      id: 1,
      name: "Manuel",
      surname: "Romero"
    });
  });

const countUserFriends = () => new Promise((resolve, reject) => resolve(50));

const getUserSkills = () =>
  new Promise((resolve, reject) => {
    resolve({
      skills: ["JavaScript", "ES6", "MongoDB", "Angular.js"]
    });
  });

Promise.all([getUserInfo(), countUserFriends(), getUserSkills()])
  .then(results => {
    const userInfo = results[0];
    const numberOfFriends = results[1];
    const userSkills = results[2];

    console.log(userInfo);
    console.log(numberOfFriends);
    console.log(userSkills);
  })
  .catch(err => console.log(err));

In the code above, we have 3 promises that can be launched in parallel: the first is to get the user’s basic information. The second is to get the number of friends of the user and the last one to get the user’s skills.
As we can see, the variable that contains the result of the Promise.all() function is named results. Results is an array with so many positions as promises were sent as parameter to Promise.all(). Moreover, the result of the first promise goes in the first position of the array, the result of the second promise goes in the second position and so on.
But what I did to improve readability was assigning each result (position) of the array to a new variable. Because access to results[n] is not meaningful.
Now, let’s see how it can be done directly with ES6 array destructing:

"use strict";

const getUserInfo = () =>
  new Promise((resolve, reject) => {
    resolve({
      id: 1,
      name: "Manuel",
      surname: "Romero"
    });
  });

const countUserFriends = () => new Promise((resolve, reject) => resolve(50));

const getUserSkills = () =>
  new Promise((resolve, reject) => {
    resolve({
      skills: ["JavaScript", "ES6", "MongoDB", "Angular.js"]
    });
  });

Promise.all([getUserInfo(), countUserFriends(), getUserSkills()])
  .then(([userInfo, userFriends, userSkills]) => {
    console.log(userInfo);
    console.log(userFriends);
    console.log(userSkills);
  })
  .catch(err => console.log(err));

In the code above, I give a name for each position of the array. These names are the variables that we can manage in the callback. This way we save code and the most important thing, we write readable code easy to understand and easy to maintain.
PS: The two snippets (above) in this post were tested under Node v6.

Async/await version (extra for dev.to friends) :

"use strict";

const getUserInfo = () =>
  new Promise((resolve, reject) => {
    resolve({
      id: 1,
      name: "Manuel",
      surname: "Romero"
    });
  });

const countUserFriends = () => new Promise((resolve, reject) => resolve(50));

const getUserSkills = () =>
  new Promise((resolve, reject) => {
    resolve({
      skills: ["JavaScript", "ES6", "MongoDB", "Angular.js"]
    });
  });

const getAllUserData = async () => {
  try {
    const [userInfo, userFriends, userSkills] = await Promise.all([
      getUserInfo(),
      countUserFriends(),
      getUserSkills()
    ]);

    const userData = {
      userInfo,
      userFriends,
      userSkills
    };
    return userData;

  } catch (error) {
    return Promise.reject(error);
  }
};

/* MAIN */
getAllUserData()
  .then(userData => console.log(userData))
  .catch(error => console.log(error));

Discussion

markdown guide
 

ES6 allows the reverse as well which means the method above can be even neater, Also since the entire method is marked async there is no need for the try/catch if your not interested in the error (as in the example above).

async function getAllUserData() {
  let [ userInfo, userFriends, userSkills ] = await Promise.all([
    getUserInfo(),
    countUserFriends(),
    getUserSkills()
  ];
  return {userInfo, userFriends, userSkills};
}

Some Promise libraries offer a hash function for this purpose:

async function getAllUserData() {
  return RSVP.hash({
    userInfo: getUserInfo(),
    userFriends: countUserFriends(),
    userSkills: getUserSkills()
  });
}
 

It does not make sense to create a rejectable Promise if it cannot be rejectable. So you can use directly:

return Promise.resolve("whatever");

//instead of

return new Promise((resolve, reject)=> {
 resolve("whatever");
});