Beautiful way to resolve an object with nested promises? Beautiful way to resolve an object with nested promises? express express

Beautiful way to resolve an object with nested promises?


You could use the helper function below. It takes an object and returns a promise that resolves when all nested promises have been resolved. The returned promise will provide as value the same object, which will have mutated with all its embedded promises replaced by their corresponding values.

function promiseRecursive(obj) {    const getPromises = obj =>        Object.keys(obj).reduce( (acc, key) =>            Object(obj[key]) !== obj[key]                ? acc                : acc.concat(                    typeof obj[key].then === "function"                        ? [[obj, key]]                        : getPromises(obj[key])                  )        , []);    const all = getPromises(obj);    return Promise.all(all.map(([obj, key]) => obj[key])).then( responses =>        (all.forEach( ([obj, key], i) => obj[key] = responses[i]), obj)     );}

You would call it like this:

var loginResponsePromise = promiseRecursive({    userprofile : getProfile(10),    companyInfo : {        company : getCompany(101),        companyRelations : getPriviligedInfo(101)    },    groups : getGroups([5])});

function promiseRecursive(obj) {    const getPromises = obj =>        Object.keys(obj).reduce( (acc, key) =>            Object(obj[key]) !== obj[key] ? acc                : acc.concat(typeof obj[key].then === "function" ? [[obj, key]]                 : getPromises(obj[key]))        , []);    const all = getPromises(obj);    return Promise.all(all.map(([obj, key]) => obj[key])).then( responses =>        (all.forEach( ([obj, key], i) => obj[key] = responses[i]), obj)     );}// Example promise-returning functionsconst wait = ms => new Promise( resolve => setTimeout(resolve, ms) ),    getProfile = id => wait(100).then(_ => ({userName: 'user' + id,id})),    getCompany = employeeId => wait(200).then(_ => ({employeeName: 'employee' + employeeId, employeeId})),    getPriviligedInfo = employeeId => wait(500).then(_ => ({privs: 'privInfo' + employeeId, employeeId})),    getGroups = memberGroupsIds => wait(400).then(_ => ({groups: ['group' + memberGroupsIds[0]],memberGroupsIds}));// Sample input passed to `promiseRecursive` functionconst loginResponsePromise = promiseRecursive({    userprofile : getProfile(10),    companyInfo : {        company : getCompany(101),        companyRelations : getPriviligedInfo(101)    },    groups : getGroups([5])});// Display the resolved objectloginResponsePromise.then( o => console.log(o) );
.as-console-wrapper { max-height: 100% !important; top: 0; }


I usually solve this kind of scenarios with Bluebird's join http://bluebirdjs.com/docs/api/promise.join.html :

const Promise = require('bluebird');return Promise.join(    getProfile(id),    getCompany(employeeId),    getPrivilegedInfo(employeeId),    getGroups(memberGroupsIds),    (userProfile, company, companyRelations, groups) => {        return {            userProfile: userProfile,            companyInfo: {                company: company,                companyRelations: companyRelations            },            groups: groups        };    });


Using new ES6 features I would write something like this:

Promise.all([    getProfile(id),    getCompany(employeeId),    getPriviligedInfo(employeeId),    getGroups(memberGroupsIds)]).then(response => {    const [ userprofile, company, companyRelations, groups ] = response    const loginResponse = {      userprofile,      companyInfo : {        company,        companyRelations      },      groups    }}).catch(err => console.error(err))

Maybe the interesting part is that Promise.all() keep the input arguments order not depending on which resolves first. So in next step, using Destructuring Array assignment, the code looks like synchronous.