不要在顺序链接的承诺中包含 q 承诺结果
Don't include q promise result in sequentially chained promises
我有以下按顺序链接的 Q 承诺,它填充了用户->属性->租户->提醒的层次结构:
return getAllUsers() // get all users of the system
.then(function(users) {
return Q.all(users.map(function(user) {
return getUserProperties(user) // foreach user of the system get their properties
.then(function(properties) {
user.properties = properties;
return Q.all(properties.map(function(property) {
return getPropertyTenants(property) // foreach property get active tenants
.then(function(tenants) {
property.tenants = tenants;
return Q.all(tenants.map(function(tenant) {
return getTenantReminders(property, tenant) // foreach tenant get their payment reminders
.then(function(reminders) {
// if reminders.length == 0 we don't want to include this user
tenant.reminders = reminders;
});
}));
});
}));
}).thenResolve(user);
}));
});
这个承诺链的要点是获取租户的所有付款提醒,然后向每个用户发送一封电子邮件,其中包含租户提醒列表(每个用户)。如果没有从 getTenantReminders() 解析的提醒,那么理想情况下,来自顶级 getAllUsers() 的用户将不会包含在结果中。
我考虑过从 getTenantReminders().then() 处理程序中抛出一个特殊错误,该处理程序沿链向上并在 thenResolve(user) 中处理,然后 return 用户。不确定这是否是正确的方法。
感谢任何建议。
正如赛斯所说,在这里保持理智的关键是避免厄运金字塔。这是我认为应该可行的方法(尽管显然我还没有测试过):
// Produces a function that takes an array of values as input,
// Applies func() to each of them, and merges the results into a
// promise for a single array.
// func() is assumed to take a single value as input and
// return an array or promise for an array
// (X -> Promise<Y[]>) -> (X[] -> Promise<Y[]>)
function applyAllAndMerge(func) {
return function (values) {
return Q.all(values.map(func))
.then(function (arrays) {
return Array.prototype.concat.apply([], arrays);
});
};
}
// returns a promise for an array of all tenant reminders for the
// specified property
// property -> Promise<tenantReminder[]>
function getTenantRemindersForProperty(property) {
return getPropertyTenants(property)
.then(applyAllAndMerge(function (tenant) {
return getTenantReminders(property, tenant);
}));
}
// returns a promise for an array of all tenant reminders for the
// specified user
// user -> Promise<tenantReminder[]>
function getTenantRemindersForUser(user) {
return getUserProperties(user)
.then(applyAllAndMerge(getTenantRemindersForProperty));
}
// returns a promise for an object containing the specified
// user and all of their tenant reminders
// user -> Promise<{user, tenantReminder}[]>
function getUserAndTenants(user) {
return getTenantRemindersForUser(user)
.then(function (reminders) {
return { user: user, reminders: reminders };
});
}
return getAllUsers() // get all users of the system
.then(function(users) {
return Q.all(users.map(getUserAndTenants));
})
.then(function (userReminders) {
// get rid of users with no reminders
var usersWithReminders = userReminders.filter(function (ur) {
return ur.reminders.length > 0;
});
// use usersWithReminders
});
我有以下按顺序链接的 Q 承诺,它填充了用户->属性->租户->提醒的层次结构:
return getAllUsers() // get all users of the system
.then(function(users) {
return Q.all(users.map(function(user) {
return getUserProperties(user) // foreach user of the system get their properties
.then(function(properties) {
user.properties = properties;
return Q.all(properties.map(function(property) {
return getPropertyTenants(property) // foreach property get active tenants
.then(function(tenants) {
property.tenants = tenants;
return Q.all(tenants.map(function(tenant) {
return getTenantReminders(property, tenant) // foreach tenant get their payment reminders
.then(function(reminders) {
// if reminders.length == 0 we don't want to include this user
tenant.reminders = reminders;
});
}));
});
}));
}).thenResolve(user);
}));
});
这个承诺链的要点是获取租户的所有付款提醒,然后向每个用户发送一封电子邮件,其中包含租户提醒列表(每个用户)。如果没有从 getTenantReminders() 解析的提醒,那么理想情况下,来自顶级 getAllUsers() 的用户将不会包含在结果中。
我考虑过从 getTenantReminders().then() 处理程序中抛出一个特殊错误,该处理程序沿链向上并在 thenResolve(user) 中处理,然后 return 用户。不确定这是否是正确的方法。
感谢任何建议。
正如赛斯所说,在这里保持理智的关键是避免厄运金字塔。这是我认为应该可行的方法(尽管显然我还没有测试过):
// Produces a function that takes an array of values as input,
// Applies func() to each of them, and merges the results into a
// promise for a single array.
// func() is assumed to take a single value as input and
// return an array or promise for an array
// (X -> Promise<Y[]>) -> (X[] -> Promise<Y[]>)
function applyAllAndMerge(func) {
return function (values) {
return Q.all(values.map(func))
.then(function (arrays) {
return Array.prototype.concat.apply([], arrays);
});
};
}
// returns a promise for an array of all tenant reminders for the
// specified property
// property -> Promise<tenantReminder[]>
function getTenantRemindersForProperty(property) {
return getPropertyTenants(property)
.then(applyAllAndMerge(function (tenant) {
return getTenantReminders(property, tenant);
}));
}
// returns a promise for an array of all tenant reminders for the
// specified user
// user -> Promise<tenantReminder[]>
function getTenantRemindersForUser(user) {
return getUserProperties(user)
.then(applyAllAndMerge(getTenantRemindersForProperty));
}
// returns a promise for an object containing the specified
// user and all of their tenant reminders
// user -> Promise<{user, tenantReminder}[]>
function getUserAndTenants(user) {
return getTenantRemindersForUser(user)
.then(function (reminders) {
return { user: user, reminders: reminders };
});
}
return getAllUsers() // get all users of the system
.then(function(users) {
return Q.all(users.map(getUserAndTenants));
})
.then(function (userReminders) {
// get rid of users with no reminders
var usersWithReminders = userReminders.filter(function (ur) {
return ur.reminders.length > 0;
});
// use usersWithReminders
});