简体   繁体   English

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

[英]Beautiful way to resolve an object with nested promises?

While building custom endpoints I often need to resolve a complex object containing promises. 在构建自定义端点时,我经常需要解析包含promises的复杂对象。

For illustration, take this example: 为了便于说明,请举例:

Given known user's id , employeeId and memberGroupsIds (an array): 给定已知用户的idemployeeIdmemberGroupsIds (数组):

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

This logic works for synchronous functions that just return their values. 此逻辑适用于仅返回其值的同步函数。 But with functions that return promises I have to manually push all of them into an array to ensure they are resolved before using the final object. 但是对于返回promises的函数,我必须手动将所有这些函数推送到一个数组中,以确保在使用final对象之前解析它们。

I find the above code very easy to understand, and I'm looking for a signature that gives some of that, while still ensuring that the promises are resolved before sending a final object to the client. 我发现上面的代码很容易理解,我正在寻找一个签名,它提供了一些,同时仍然确保在将最终对象发送到客户端之前解析了promises。

The problem is not making it work, but making it beautiful and easy to read. 问题不是使它工作,而是使它美观和易于阅读。

The best answer would ensure that the values are returned to the expected keys in the object and that all the promises are resolved in parallel, while maintaining a structure that is somewhat compatible with that of synchronous functions. 最佳答案将确保将值返回到对象中的预期键,并确保所有promises并行解析,同时保持与同步函数的某种程度兼容的结构。

Or, if I'm missing the point and looking at this all wrong, how should I be looking at it? 或者,如果我错过了这一点并且看着这一切都错了,我该怎么看?

You could use the helper function below. 你可以使用下面的辅助函数。 It takes an object and returns a promise that resolves when all nested promises have been resolved. 它接受一个对象并返回一个promise,该promise在解析所有嵌套的promises时解析。 The returned promise will provide as value the same object, which will have mutated with all its embedded promises replaced by their corresponding values. 返回的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) 
    );
}

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

I usually solve this kind of scenarios with Bluebird's join http://bluebirdjs.com/docs/api/promise.join.html : 我通常使用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
        };
    }
);

Using new ES6 features I would write something like this: 使用新的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))

Maybe the interesting part is that Promise.all() keep the input arguments order not depending on which resolves first. 也许有趣的部分是Promise.all()保持输入参数顺序不依赖于首先解析。 So in next step, using Destructuring Array assignment , the code looks like synchronous. 因此,在下一步中,使用Destructuring Array赋值 ,代码看起来像是同步的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM