问题如下。我有一组像这样的对象:
let myObj = [
{'db1':['doc1','doc2','doc3']},
{'db2':['doc4','doc5']},
{'db3':['doc7','doc8','doc9','doc10']}
]
请注意,这是我决定用于解决问题的数据结构,如果可以改进整体实现,则可以更改。实际的db和doc ID是从格式如下的文本文件中读取的。
"db1","doc1"
"db1","doc2"
...
我的应用将同步遍历数据库列表。在每次db迭代中,都会有文档列表的异步迭代。将检索,处理每个文档并将其保存回数据库。
所以基本上在任何给定的实例中:一个数据库,但是多个文档。
我有如上所述的工作实现:
dbIterator :迭代dbs的同步外循环。传递给docIterator
的回调将触发下一次迭代。
const dbIterator = function (x) {
if (x < myObj.length) {
let dbObj = myObj[x];
let dbId = Object.keys(dbObj)[0];
docIterator(dbId, dbObj[dbId], ()=>merchantIterator(x+1));
} else {
logger.info('All dbs processed');
}
};
docIterator :迭代文档的异步循环。在处理完所有文档后调用回调cb
。这是通过docsProcessed
和docsToBeProcessed
变量
const docIterator = function(dbId, docIds, cb){
//create connection
targetConnection = //some config for connection to dbId
let docsProcessed = 0;
let docsToBeProcessed = docIds.length;
//asynchronous iteration of documents
docIds.forEach((docId)=>{
getDocument(docId, targetConnection).then((doc)=>{
//process document
processDoc(doc, targetConnection).then(()=>{
//if processing is successful
if (++docsProcessed >= docsToBeProcessed) {
cb();
}
})
//if processing fails
.catch((e) => {
logger.error('error when processing document');
if (++docsProcessed >= docsToBeProcessed) {
cb();
}
});
}).catch((e)=>{
logger.error('error when retrieving document: ');
if (++docsProcessed >= docsToBeProcessed) {
cb();
}
});
});
};
processDoc:用于处理和保存单个文档。这将返回一个在文档处理完成时得到解决的承诺,该承诺依次递增docsProcessed
并且有条件地(docsProcessed >= docsToBeProcessed
)调用回调传递给docIterator
const processDoc = function(doc, targetConnection) {
return new Promise(function(resolve, reject) {
if(shouldThisDocBeProcessed(doc){
let updatedDoc = logic(doc);
targetConnection.insert(updatedDoc, updatedDoc._id,
function (error, response) {
if (!error){
logger.info('updated successfully');
} else {
logger.error('error when saving doc');
}
resolve();
}
);
} else {
resolve();
}
})
};
这可以按预期工作,但对我来说,这种实现是次优和杂乱的。我非常确定这可以改进,最重要的是有机会更好地理解和实现同步和异步问题的解决方案。
我愿意接受建设性的批评。那么如何改进呢?
答案 0 :(得分:0)
也许是这样的?
可以找到节流的示例实现here。
//this should be available in both modules so you can filter
const Fail = function(details){this.details=details;};
// docIterator(dbId,docIds)
// .then(
// results =>{
// const failedResults = results.filter(
// result => (result&&result.constructor)===Failed
// );
// const successfullResults = results.filter(
// result => (result&&result.constructor)!==Failed
// );
// }
// )
const docIterator = function(dbId, docIds){
//create connection
// targetConnection = //some config for connection to dbId
let docsProcessed = 0;
let docsToBeProcessed = docIds.length;
//asynchronous iteration of documents
docIds.map(
docId =>
new Promise(
(resolve,reject) =>
//if you use throttled you can do:
// max10(
// ([docId,targetConnection])=>
// getDocument(docId,targetConnection)
// )([docId, targetConnection])
getDocument(docId, targetConnection)
)
.then(
doc =>
//if this returns nothing then maybe you'd like to return the document
processDoc(doc, targetConnection)
.then(
_ => doc
)
)
.catch(
err => new fail([err,docId])
)
)
};