如何改进此嵌套异步循环?

时间:2017-12-09 10:04:15

标签: javascript node.js asynchronous callback promise

问题如下。我有一组像这样的对象:

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。这是通过docsProcesseddocsToBeProcessed变量

跟踪的
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();
    }
  })
};

这可以按预期工作,但对我来说,这种实现是次优和杂乱的。我非常确定这可以改进,最重要的是有机会更好地理解和实现同步和异步问题的解决方案。

我愿意接受建设性的批评。那么如何改进呢?

1 个答案:

答案 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])
      )

  )
};