使用嵌套承诺解析对象的美妙方法?

时间:2017-05-19 08:53:30

标签: javascript express promise loopbackjs bluebird

在构建自定义端点时,我经常需要解析包含promises的复杂对象。

为了举例说明,请举例:

鉴于已知用户的 id employeeId memberGroupsIds (数组):

var loginResponse = {
    userprofile : getProfile(id)
    companyInfo : {
        company : getCompany(employeeId)
        companyRelations : getPriviligedInfo(employeeId)
    }
    groups : getGroups(memberGroupsIds)
}

此逻辑适用于仅返回其值的同步函数。但是对于返回promise的函数,我必须手动将所有函数推送到数组中,以确保在使用最终对象之前解析它们。

我发现上面的代码很容易理解,而且我正在寻找一个签名来提供其中一些,同时仍然确保在将最终对象发送到客户端之前解析了promises。

问题不在于它是否有效,而是使其美观且易于阅读。

最佳答案将确保将值返回到对象中的预期键,并确保所有promises并行解析,同时保持与同步函数的某种程度兼容的结构。

或者,如果我错过了这一点并且看错了,我应该怎么看?

3 个答案:

答案 0 :(得分:2)

您可以使用下面的辅助功能。它接受一个对象并返回一个promise,该promise在解析所有嵌套的promises时解析。返回的promise将提供相同对象的值,该对象将被变异,其所有嵌入的promise将被其相应的值替换。

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) 
    );
}

你会这样称呼:

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 functions
const 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` function
const loginResponsePromise = promiseRecursive({
    userprofile : getProfile(10),
    companyInfo : {
        company : getCompany(101),
        companyRelations : getPriviligedInfo(101)
    },
    groups : getGroups([5])
});

// Display the resolved object
loginResponsePromise.then( o => console.log(o) );

.as-console-wrapper { max-height: 100% !important; top: 0; }




答案 1 :(得分:1)

我通常使用Bluebird的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
        };
    }
);

答案 2 :(得分:0)

使用新的ES6功能我会写这样的东西:

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))

也许有趣的部分是Promise.all()保持输入参数顺序不依赖于首先解析。因此,在下一步中,使用Destructuring Array assignment,代码看起来像是同步的。