Reputation: 381
I have this snippet of working code that I really want to simplify. It is targeted for Node.js v6.0 with the packages bluebird & co. This is a typical situation and I would like to get to the point that the code is compact and easy to understand so I can reuse it in other projects.
The function returns one checklist with a number of item categories (1:m), and each category contains a number of items (1:m:n). So it is an object with 2 nested levels:
Root: Checklist
Level 1: Checklist Item Group(s)
Level 2: Checklist Item(s)
The root level (1 checklist) and one level down (checklist item categories) can be implemented in the generator function by yielding.
The 1:n:m level cannot use "yield" when I would use e.g. Promise.mapSeries() over checklist.__node__ChecklistItemGroups because a "yield" is only supported in the main generator function, not in the map inner function. So I reverted back to the function Promise.each() and a Promise.then() construct.
function getGraphChecklistById(pId) {
return co(function *() {
let checklist = yield getChecklistById(pId); // Returns a promise.
checklist.__node__ChecklistItemGroups = yield fetchChecklistItemGroupsByChecklistId(pId); // Returns a promise.
return checklist;
})
.then(function (checklist) {
return Promise.each(
checklist.__node__ChecklistItemGroups,
function (pItem) {
return fetchChecklistItemsByXrefId(pItem.xchlclig_id).then(function (result) { // fetchChecklistItemsByXrefId() returns a promise.
pItem.__node__ChecklistItems = result;
});
})
.then(function (unused) {
return checklist;
});
})
}
This is an example result:
result: {
"checklist-field-1": 1,
"checklist-field-2": 1,
"__node__ChecklistItemGroups": [
{
"checklist-group-field-1": 1,
"checklist-group-field-2": 1,
"__node__ChecklistItems": [
{
"checklist-item-field-1": 1,
"checklist-item-field-2": 1,
},
{
"checklist-item-field-1": 2,
"checklist-item-field-2": 2,
}
]
},
{
"checklist-group-field-1": 2,
"checklist-group-field-2": 2,
"__node__ChecklistItems": [
{
"checklist-item-field-1": 1,
"checklist-item-field-2": 1,
},
{
"checklist-item-field-1": 2,
"checklist-item-field-2": 2,
}
]
}
]
}
Any suggestions from a Promise/Coroutines expert? Thanks for your time.
Upvotes: 2
Views: 638
Reputation: 664548
I'd write it like this - without Promise.each
but a plain loop:
let getGraphChecklistById = Promise.coroutine(function*(pId) {
// ^^^^^^^^^^^^^^^^^ Don't put co(…) in a function, wrap directly
let checklist = yield getChecklistById(pId);
checklist.__node__ChecklistItemGroups = yield fetchChecklistItemGroupsByChecklistId(pId); // Returns a promise.
for (let pItem of checklist.__node__ChecklistItemGroups) {
// ^^^ iteration in series like Promise.each does it
let result = yield fetchChecklistItemsByXrefId(pItem.xchlclig_id);
// ^^^^^ You can use yield here as it's not an extra function
pItem.__node__ChecklistItems = result;
}
return checklist;
});
Whether you use Bluebird's Promise.coroutine
or co.wrap
doesn't really matter here.
And if you want to iterate the groups in parallel, you still can use yield
in the callback function if you make that a generator:
let getGraphChecklistById = Promise.coroutine(function*(pId) {
let checklist = yield getChecklistById(pId);
checklist.__node__ChecklistItemGroups = yield fetchChecklistItemGroupsByChecklistId(pId); // Returns a promise.
yield Promise.map(
checklist.__node__ChecklistItemGroups,
Promise.coroutine(function*(pItem) {
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ pass this as the promise-returning callback function
let result = yield fetchChecklistItemsByXrefId(pItem.xchlclig_id);
pItem.__node__ChecklistItems = result;
})
);
return checklist;
});
Upvotes: 4