解决带有嵌套承诺的对象的美丽方法?
Beautiful way to resolve an object with nested promises?
在构建自定义端点时,我经常需要解析包含承诺的复杂对象。
举例说明:
给定已知用户的 id、employeeId 和 memberGroupsIds(数组):
var loginResponse = {
userprofile : getProfile(id)
companyInfo : {
company : getCompany(employeeId)
companyRelations : getPriviligedInfo(employeeId)
}
groups : getGroups(memberGroupsIds)
}
此逻辑适用于仅 return 它们的值的同步函数。但是对于 return 承诺的函数,我必须手动将它们全部推送到一个数组中,以确保在使用最终对象之前解析它们。
我发现上面的代码非常容易理解,我正在寻找一个签名来提供其中的一些,同时仍然确保在将最终对象发送给客户端之前解决承诺。
问题不在于让它发挥作用,而在于让它美观易读。
最好的答案是确保将值 return 编辑到对象中的预期键,并确保并行解决所有承诺,同时保持与同步函数的结构在某种程度上兼容的结构.
或者,如果我没抓住重点,看错了,我应该怎么看?
您可以使用下面的辅助函数。它需要一个对象和 returns 一个 promise,当所有嵌套的 promises 都被解析时,该 promise 就会解析。返回的 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; }
我通常用 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
};
}
);
使用新的 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,代码看起来是同步的。
在构建自定义端点时,我经常需要解析包含承诺的复杂对象。
举例说明:
给定已知用户的 id、employeeId 和 memberGroupsIds(数组):
var loginResponse = {
userprofile : getProfile(id)
companyInfo : {
company : getCompany(employeeId)
companyRelations : getPriviligedInfo(employeeId)
}
groups : getGroups(memberGroupsIds)
}
此逻辑适用于仅 return 它们的值的同步函数。但是对于 return 承诺的函数,我必须手动将它们全部推送到一个数组中,以确保在使用最终对象之前解析它们。
我发现上面的代码非常容易理解,我正在寻找一个签名来提供其中的一些,同时仍然确保在将最终对象发送给客户端之前解决承诺。
问题不在于让它发挥作用,而在于让它美观易读。
最好的答案是确保将值 return 编辑到对象中的预期键,并确保并行解决所有承诺,同时保持与同步函数的结构在某种程度上兼容的结构.
或者,如果我没抓住重点,看错了,我应该怎么看?
您可以使用下面的辅助函数。它需要一个对象和 returns 一个 promise,当所有嵌套的 promises 都被解析时,该 promise 就会解析。返回的 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; }
我通常用 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
};
}
);
使用新的 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,代码看起来是同步的。