【问题标题】:Is there a way to do asynch calls recursively with only one callback call?有没有办法只用一个回调调用递归地进行异步调用?
【发布时间】:2018-07-24 16:40:27
【问题描述】:

我想知道是否有人知道在 javascript 中递归执行异步调用的方法。我正在尝试一次将批量数据上传到 cloudant 数据库 500 个项目。我不知道 json 会有多少个文档(介于 2000-4000 之间),所以我创建了一个递归函数将它分成 500 个块并上传。

insertDBBulkData: function(trgtDB, dbDocData, callback) {

    // if length > 500, recursively upload 500 at a time
    if(dbDocData.length > 500) {
        console.log("File too big. Length " + dbDocData.length)
        module.exports.insertDBBulkData(trgtDB, dbDocData.slice(500, dbDocData.length), function(err, body) {
            if(err) {   
                console.log(err);
                callback(err, body)
            } else {
                // only callback on last one
                callback(err, body);
            }
        });

        dbDocData = dbDocData.slice(0, 500);
    }

    trgtDB.bulk({"docs": dbDocData}, function(err, body) {
        if(err) {
            callback(err);
        } else {
            console.log("Successfully uploaded " + dbDocData.length + " users. ")
            callback(null, "Success!")
        }
    });
},

问题是:因为我不知道哪个调用会最后完成,所以我不知道何时将响应发送回服务器(我只能执行一次)。我尝试过使用 Promises,但据我所知,我不能将 Promises 与这种递归方法一起使用,因为我没有固定的调用次数。这是可以通过延迟承诺实现的吗?

感谢任何帮助。 谢谢!

解决方案:

感谢 Jonas W。我能够在 for 循环中使用多个 Promise 来执行此操作。它不使用递归,但效果更好。这是我的解决方案:

insertDBBulkData: function(trgtDB, dbDocData, callback) {
    const promises = [];

    for(let start = 0; start < dbDocData.length; start += 500) {
        console.log("File too big. Index: " + start);

        var dbBulkDataPromise = new Promise(function(resolve, reject) {
            trgtDB.bulk({"docs": dbDocData.slice(start, start+500)}, function(err, body) {
                if(err) {
                    reject(err);
                } else {
                    resolve("Success")
                }
            });
        });

        promises.push(dbBulkDataPromise);
    }

    Promise.all(promises).then(function(values) {
        console.log(values);
        var completed = true;

        for (message in values) {
            if (message != "Success") {
                completed = false;
            }
        }

        if (completed) {
            callback(null, values)
        } else {
            console.log("Partial upload")
            callback("Partial upload only", null)
        }


    }).catch(function(err) {
        console.log("Error uploading data: " + err);
        callback(err, null)
    });
},

【问题讨论】:

  • 您将所有内容并行插入到数据库中,只是在不同的块中。这真的是你想做的吗?还是你打算一个接一个地做?
  • 一切都可以并行插入,因为我稍后会查询它进行排序。
  • 那你为什么要拆分成多个调用呢?
  • 对所有数据的一次 cloudant db 调用太大而无法一次发送。它给了我一个错误。 stackoverflow.com/questions/33937631/…
  • 你能发送总计数与数据组和收到的更新计数吗

标签: javascript express asynchronous recursion cloudant


【解决方案1】:

如果你将一个批量的插入包装到一个承诺中:

function insert(docs) {
   return new Promise((resolve, reject) => {
      trgtDB.bulk({ docs }, function(err, body) {
        if(err) {
          reject(err);
        } else {
           console.log("Successfully uploaded " + docs.length + " users. ")
           resolve("Success!")
       }
   });
  });
}

现在您可以查看大批量数据,将其分割成块,为每个块开始插入,然后等待所有承诺:

const promises = [], size = 500;

for(let start = 0; start < dbDocData.length; start += size)
  promises.push(insert( dbDocData.slice(start, start + size) ));

Promise.all(promises).then(() => {
  console.log("all done");
}).catch(/* handle errors */);

【讨论】:

  • 这太棒了!完美运行。我将用我的完整解决方案更新问题。
【解决方案2】:

这是一个使用 Promise 的简单示例:

    function insertData(trgtDB, dbDocData, callback) {
      var batchSize = 500;

      while (dbDocData.length > 0) {
        var batch = dbDocData.slice(0, batchSize);
        dbDocData.splice(0, batchSize);

        insertBatch(targetDB, dbDocData.slice(500, dbDocData.length))
          .then(function(result) {
            // everything inserted just fine
            if (dbDocData.length < 1){
              callback(yourParams);
            }
          })
          .error(function(err) {
            // handle your error
          });
      }

    }

    function insertBatch(trgtBD, batch) {
      // return a new promise 
      return new Promise(function(resolve, reject) {
        // load into the db
        trgtDB.bulk({
          "docs": batch
        }, function(err, body) {
          if (err) {
            // there was an error reject the promise
            reject(err);
          } else {
            // everything is good, 
            resolve("Success!")
          }
        });
      })
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-04-18
    • 2018-01-30
    • 1970-01-01
    • 2021-01-04
    • 2019-06-30
    • 1970-01-01
    • 2019-05-16
    • 2017-10-28
    相关资源
    最近更新 更多