如何使用嵌套协程和 promises 简化 JS 代码?
How to simplify JS code with nested coroutines and promises?
我有这个工作代码片段,我真的很想简化它。它的目标是 Node.js v6.0,包 bluebird & co。这是一个典型的情况,我想说的是代码紧凑且易于理解,以便我可以在其他项目中重用它。
函数returns一个清单,其中包含多个项目类别(1:m),每个类别包含多个项目(1:m:n)。所以它是一个 具有 2 个嵌套级别的对象 :
Root: Checklist
Level 1: Checklist Item Group(s)
Level 2: Checklist Item(s)
根级(1个清单)和下一级(清单项目类别)可以通过yield在生成器函数中实现。
1:n:m 级别不能使用 "yield" 而我会使用例如Promise.mapSeries() 优于 checklist.__node__ChecklistItemGroups 因为 "yield" 仅在主生成器函数中受支持,在映射内部函数中不受支持。所以我又回到函数 Promise.each() 和 Promise.then() 构造。
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;
});
})
}
这是一个示例结果:
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,
}
]
}
]
}
Promise/Coroutines 专家有什么建议吗?谢谢你的时间。
我会这样写 - 没有 Promise.each
而是一个简单的循环:
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;
});
使用 Bluebird 的 Promise.coroutine
还是 co.wrap
在这里并不重要。
如果您想并行迭代这些组,您仍然可以在回调函数中使用 yield
如果您将其设为生成器:
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;
});
我有这个工作代码片段,我真的很想简化它。它的目标是 Node.js v6.0,包 bluebird & co。这是一个典型的情况,我想说的是代码紧凑且易于理解,以便我可以在其他项目中重用它。
函数returns一个清单,其中包含多个项目类别(1:m),每个类别包含多个项目(1:m:n)。所以它是一个 具有 2 个嵌套级别的对象 :
Root: Checklist
Level 1: Checklist Item Group(s)
Level 2: Checklist Item(s)
根级(1个清单)和下一级(清单项目类别)可以通过yield在生成器函数中实现。
1:n:m 级别不能使用 "yield" 而我会使用例如Promise.mapSeries() 优于 checklist.__node__ChecklistItemGroups 因为 "yield" 仅在主生成器函数中受支持,在映射内部函数中不受支持。所以我又回到函数 Promise.each() 和 Promise.then() 构造。
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;
});
})
}
这是一个示例结果:
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,
}
]
}
]
}
Promise/Coroutines 专家有什么建议吗?谢谢你的时间。
我会这样写 - 没有 Promise.each
而是一个简单的循环:
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;
});
使用 Bluebird 的 Promise.coroutine
还是 co.wrap
在这里并不重要。
如果您想并行迭代这些组,您仍然可以在回调函数中使用 yield
如果您将其设为生成器:
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;
});